diff options
author | Baolin Wang <baolin.wang@linaro.org> | 2018-04-24 20:06:11 +0800 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2019-12-11 22:06:15 +0100 |
commit | 3ddee7f88aaf2dee38f7016ac8fd48dd9fdb43e3 (patch) | |
tree | bd68506d27a4db945608bd80a7350dc5c3fa5716 /sound/core | |
parent | a4e7dd35b9dac21fa7b33e8788b51c7fbc7a49f1 (diff) | |
download | linux-3ddee7f88aaf2dee38f7016ac8fd48dd9fdb43e3.tar.bz2 |
ALSA: Avoid using timespec for struct snd_pcm_status
The struct snd_pcm_status will use 'timespec' type variables to record
timestamp, which is not year 2038 safe on 32bits system.
Userspace will use SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
as commands to issue ioctl() to fill the 'snd_pcm_status' structure in
userspace. The command number is always defined through _IOR/_IOW/IORW,
so when userspace changes the definition of 'struct timespec' to use
64-bit types, the command number also changes.
Thus in the kernel, we now need to define two versions of each such ioctl
and corresponding ioctl commands to handle 32bit time_t and 64bit time_t
in native mode:
struct snd_pcm_status32 {
......
s32 trigger_tstamp_sec;
s32 trigger_tstamp_nsec;
......
s32 audio_tstamp_sec;
s32 audio_tstamp_nsec;
......
};
struct snd_pcm_status64 {
......
s32 trigger_tstamp_sec;
s32 trigger_tstamp_nsec;
......
s32 audio_tstamp_sec;
s32 audio_tstamp_nsec;
......
};
Moreover in compat file, we renamed or introduced new structures to handle
32bit/64bit time_t in compatible mode. The 'struct snd_pcm_status32' and
snd_pcm_status_user32() are used to handle 32bit time_t in compat mode.
'struct compat_snd_pcm_status64' and snd_pcm_status_user_compat64() are used
to handle 64bit time_t.
The implicit padding before timespec is made explicit to avoid incompatible
structure layout between 32-bit and 64-bit x86 due to the different
alignment requirements, and the snd_pcm_status structure is now hidden
from the kernel to avoid relying on the timespec definitio definitionn
Finally we can replace SNDRV_PCM_IOCTL_STATUS and SNDRV_PCM_IOCTL_STATUS_EXT
with new commands and introduce new functions to fill new 'struct snd_pcm_status64'
instead of using unsafe 'struct snd_pcm_status'. Then in future, the new
commands can be matched when userspace changes 'timespec' to 64bit type
to make a size change of 'struct snd_pcm_status'. When glibc changes time_t
to 64-bit, any recompiled program will issue ioctl commands that the kernel
does not understand without this patch.
Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/pcm.c | 12 | ||||
-rw-r--r-- | sound/core/pcm_compat.c | 154 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 96 |
3 files changed, 138 insertions, 124 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 9a72d641743d..85db55ea49fd 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -443,7 +443,7 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, { struct snd_pcm_substream *substream = entry->private_data; struct snd_pcm_runtime *runtime; - struct snd_pcm_status status; + struct snd_pcm_status64 status; int err; mutex_lock(&substream->pcm->open_mutex); @@ -453,17 +453,17 @@ static void snd_pcm_substream_proc_status_read(struct snd_info_entry *entry, goto unlock; } memset(&status, 0, sizeof(status)); - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) { snd_iprintf(buffer, "error %d\n", err); goto unlock; } snd_iprintf(buffer, "state: %s\n", snd_pcm_state_name(status.state)); snd_iprintf(buffer, "owner_pid : %d\n", pid_vnr(substream->pid)); - snd_iprintf(buffer, "trigger_time: %ld.%09ld\n", - status.trigger_tstamp.tv_sec, status.trigger_tstamp.tv_nsec); - snd_iprintf(buffer, "tstamp : %ld.%09ld\n", - status.tstamp.tv_sec, status.tstamp.tv_nsec); + snd_iprintf(buffer, "trigger_time: %lld.%09lld\n", + status.trigger_tstamp_sec, status.trigger_tstamp_nsec); + snd_iprintf(buffer, "tstamp : %lld.%09lld\n", + status.tstamp_sec, status.tstamp_nsec); snd_iprintf(buffer, "delay : %ld\n", status.delay); snd_iprintf(buffer, "avail : %ld\n", status.avail); snd_iprintf(buffer, "avail_max : %ld\n", status.avail_max); diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 6f9003b1869a..2671658442ea 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -168,73 +168,13 @@ static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, snd_pcm_channel_info_user(s, p) #endif /* CONFIG_X86_X32 */ -struct snd_pcm_status32 { +struct compat_snd_pcm_status64 { s32 state; - struct compat_timespec trigger_tstamp; - struct compat_timespec tstamp; - u32 appl_ptr; - u32 hw_ptr; - s32 delay; - u32 avail; - u32 avail_max; - u32 overrange; - s32 suspended_state; - u32 audio_tstamp_data; - struct compat_timespec audio_tstamp; - struct compat_timespec driver_tstamp; - u32 audio_tstamp_accuracy; - unsigned char reserved[52-2*sizeof(struct compat_timespec)]; -} __attribute__((packed)); - - -static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, - struct snd_pcm_status32 __user *src, - bool ext) -{ - struct snd_pcm_status status; - int err; - - memset(&status, 0, sizeof(status)); - /* - * with extension, parameters are read/write, - * get audio_tstamp_data from user, - * ignore rest of status structure - */ - if (ext && get_user(status.audio_tstamp_data, - (u32 __user *)(&src->audio_tstamp_data))) - return -EFAULT; - err = snd_pcm_status(substream, &status); - if (err < 0) - return err; - - if (clear_user(src, sizeof(*src))) - return -EFAULT; - if (put_user(status.state, &src->state) || - compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || - compat_put_timespec(&status.tstamp, &src->tstamp) || - put_user(status.appl_ptr, &src->appl_ptr) || - put_user(status.hw_ptr, &src->hw_ptr) || - put_user(status.delay, &src->delay) || - put_user(status.avail, &src->avail) || - put_user(status.avail_max, &src->avail_max) || - put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state) || - put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || - compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) || - compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) || - put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) - return -EFAULT; - - return err; -} - -#ifdef CONFIG_X86_X32 -/* X32 ABI has 64bit timespec and 64bit alignment */ -struct snd_pcm_status_x32 { - s32 state; - u32 rsvd; /* alignment */ - struct timespec trigger_tstamp; - struct timespec tstamp; + u8 rsvd[4]; /* alignment */ + s64 trigger_tstamp_sec; + s64 trigger_tstamp_nsec; + s64 tstamp_sec; + s64 tstamp_nsec; u32 appl_ptr; u32 hw_ptr; s32 delay; @@ -243,22 +183,26 @@ struct snd_pcm_status_x32 { u32 overrange; s32 suspended_state; u32 audio_tstamp_data; - struct timespec audio_tstamp; - struct timespec driver_tstamp; + s64 audio_tstamp_sec; + s64 audio_tstamp_nsec; + s64 driver_tstamp_sec; + s64 driver_tstamp_nsec; u32 audio_tstamp_accuracy; - unsigned char reserved[52-2*sizeof(struct timespec)]; + unsigned char reserved[52-4*sizeof(s64)]; } __packed; #define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) -static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, - struct snd_pcm_status_x32 __user *src, - bool ext) +static int snd_pcm_status_user_compat64(struct snd_pcm_substream *substream, + struct compat_snd_pcm_status64 __user *src, + bool ext) { - struct snd_pcm_status status; + struct snd_pcm_status64 status; + struct compat_snd_pcm_status64 compat_status64; int err; memset(&status, 0, sizeof(status)); + memset(&compat_status64, 0, sizeof(compat_status64)); /* * with extension, parameters are read/write, * get audio_tstamp_data from user, @@ -267,31 +211,39 @@ static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, if (ext && get_user(status.audio_tstamp_data, (u32 __user *)(&src->audio_tstamp_data))) return -EFAULT; - err = snd_pcm_status(substream, &status); + err = snd_pcm_status64(substream, &status); if (err < 0) return err; if (clear_user(src, sizeof(*src))) return -EFAULT; - if (put_user(status.state, &src->state) || - put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || - put_timespec(&status.tstamp, &src->tstamp) || - put_user(status.appl_ptr, &src->appl_ptr) || - put_user(status.hw_ptr, &src->hw_ptr) || - put_user(status.delay, &src->delay) || - put_user(status.avail, &src->avail) || - put_user(status.avail_max, &src->avail_max) || - put_user(status.overrange, &src->overrange) || - put_user(status.suspended_state, &src->suspended_state) || - put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || - put_timespec(&status.audio_tstamp, &src->audio_tstamp) || - put_timespec(&status.driver_tstamp, &src->driver_tstamp) || - put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) + + compat_status64 = (struct compat_snd_pcm_status64) { + .state = status.state, + .trigger_tstamp_sec = status.trigger_tstamp_sec, + .trigger_tstamp_nsec = status.trigger_tstamp_nsec, + .tstamp_sec = status.tstamp_sec, + .tstamp_nsec = status.tstamp_nsec, + .appl_ptr = status.appl_ptr, + .hw_ptr = status.hw_ptr, + .delay = status.delay, + .avail = status.avail, + .avail_max = status.avail_max, + .overrange = status.overrange, + .suspended_state = status.suspended_state, + .audio_tstamp_data = status.audio_tstamp_data, + .audio_tstamp_sec = status.audio_tstamp_sec, + .audio_tstamp_nsec = status.audio_tstamp_nsec, + .driver_tstamp_sec = status.audio_tstamp_sec, + .driver_tstamp_nsec = status.audio_tstamp_nsec, + .audio_tstamp_accuracy = status.audio_tstamp_accuracy, + }; + + if (copy_to_user(src, &compat_status64, sizeof(compat_status64))) return -EFAULT; return err; } -#endif /* CONFIG_X86_X32 */ /* both for HW_PARAMS and HW_REFINE */ static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, @@ -616,8 +568,8 @@ enum { SNDRV_PCM_IOCTL_HW_REFINE32 = _IOWR('A', 0x10, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32), - SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32), - SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_COMPAT32 = _IOR('A', 0x20, struct snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32 = _IOWR('A', 0x24, struct snd_pcm_status32), SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), @@ -627,10 +579,10 @@ enum { SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32), SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32), + SNDRV_PCM_IOCTL_STATUS_COMPAT64 = _IOR('A', 0x20, struct compat_snd_pcm_status64), + SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64 = _IOWR('A', 0x24, struct compat_snd_pcm_status64), #ifdef CONFIG_X86_X32 SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info), - SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32), - SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32), SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32), #endif /* CONFIG_X86_X32 */ }; @@ -680,10 +632,10 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l return snd_pcm_ioctl_hw_params_compat(substream, 0, argp); case SNDRV_PCM_IOCTL_SW_PARAMS32: return snd_pcm_ioctl_sw_params_compat(substream, argp); - case SNDRV_PCM_IOCTL_STATUS32: - return snd_pcm_status_user_compat(substream, argp, false); - case SNDRV_PCM_IOCTL_STATUS_EXT32: - return snd_pcm_status_user_compat(substream, argp, true); + case SNDRV_PCM_IOCTL_STATUS_COMPAT32: + return snd_pcm_status_user32(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT32: + return snd_pcm_status_user32(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR32: return snd_pcm_ioctl_sync_ptr_compat(substream, argp); case SNDRV_PCM_IOCTL_CHANNEL_INFO32: @@ -702,11 +654,11 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l return snd_pcm_ioctl_rewind_compat(substream, argp); case SNDRV_PCM_IOCTL_FORWARD32: return snd_pcm_ioctl_forward_compat(substream, argp); + case SNDRV_PCM_IOCTL_STATUS_COMPAT64: + return snd_pcm_status_user_compat64(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT_COMPAT64: + return snd_pcm_status_user_compat64(substream, argp, true); #ifdef CONFIG_X86_X32 - case SNDRV_PCM_IOCTL_STATUS_X32: - return snd_pcm_status_user_x32(substream, argp, false); - case SNDRV_PCM_IOCTL_STATUS_EXT_X32: - return snd_pcm_status_user_x32(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR_X32: return snd_pcm_ioctl_sync_ptr_x32(substream, argp); case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32: diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 67fe14bad7cb..ad4cf1e3e3bd 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -891,8 +891,8 @@ snd_pcm_calc_delay(struct snd_pcm_substream *substream) return delay + substream->runtime->delay; } -int snd_pcm_status(struct snd_pcm_substream *substream, - struct snd_pcm_status *status) +int snd_pcm_status64(struct snd_pcm_substream *substream, + struct snd_pcm_status64 *status) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -918,14 +918,22 @@ int snd_pcm_status(struct snd_pcm_substream *substream, status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) goto _end; - status->trigger_tstamp = timespec64_to_timespec(runtime->trigger_tstamp); + status->trigger_tstamp_sec = runtime->trigger_tstamp.tv_sec; + status->trigger_tstamp_nsec = runtime->trigger_tstamp.tv_nsec; if (snd_pcm_running(substream)) { snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - status->tstamp = runtime->status->tstamp; - status->driver_tstamp = timespec64_to_timespec(runtime->driver_tstamp); - status->audio_tstamp = - runtime->status->audio_tstamp; + status->tstamp_sec = runtime->status->tstamp.tv_sec; + status->tstamp_nsec = + runtime->status->tstamp.tv_nsec; + status->driver_tstamp_sec = + runtime->driver_tstamp.tv_sec; + status->driver_tstamp_nsec = + runtime->driver_tstamp.tv_nsec; + status->audio_tstamp_sec = + runtime->status->audio_tstamp.tv_sec; + status->audio_tstamp_nsec = + runtime->status->audio_tstamp.tv_nsec; if (runtime->audio_tstamp_report.valid == 1) /* backwards compatibility, no report provided in COMPAT mode */ snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, @@ -940,7 +948,8 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct timespec64 tstamp; snd_pcm_gettime(runtime, &tstamp); - status->tstamp = timespec64_to_timespec(tstamp); + status->tstamp_sec = tstamp.tv_sec; + status->tstamp_nsec = tstamp.tv_nsec; } } _tstamp_end: @@ -958,11 +967,11 @@ int snd_pcm_status(struct snd_pcm_substream *substream, return 0; } -static int snd_pcm_status_user(struct snd_pcm_substream *substream, - struct snd_pcm_status __user * _status, - bool ext) +static int snd_pcm_status_user64(struct snd_pcm_substream *substream, + struct snd_pcm_status64 __user * _status, + bool ext) { - struct snd_pcm_status status; + struct snd_pcm_status64 status; int res; memset(&status, 0, sizeof(status)); @@ -974,7 +983,7 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, if (ext && get_user(status.audio_tstamp_data, (u32 __user *)(&_status->audio_tstamp_data))) return -EFAULT; - res = snd_pcm_status(substream, &status); + res = snd_pcm_status64(substream, &status); if (res < 0) return res; if (copy_to_user(_status, &status, sizeof(status))) @@ -982,6 +991,55 @@ static int snd_pcm_status_user(struct snd_pcm_substream *substream, return 0; } +static int snd_pcm_status_user32(struct snd_pcm_substream *substream, + struct snd_pcm_status32 __user * _status, + bool ext) +{ + struct snd_pcm_status64 status64; + struct snd_pcm_status32 status32; + int res; + + memset(&status64, 0, sizeof(status64)); + memset(&status32, 0, sizeof(status32)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status64.audio_tstamp_data, + (u32 __user *)(&_status->audio_tstamp_data))) + return -EFAULT; + res = snd_pcm_status64(substream, &status64); + if (res < 0) + return res; + + status32 = (struct snd_pcm_status32) { + .state = status64.state, + .trigger_tstamp_sec = status64.trigger_tstamp_sec, + .trigger_tstamp_nsec = status64.trigger_tstamp_nsec, + .tstamp_sec = status64.tstamp_sec, + .tstamp_nsec = status64.tstamp_nsec, + .appl_ptr = status64.appl_ptr, + .hw_ptr = status64.hw_ptr, + .delay = status64.delay, + .avail = status64.avail, + .avail_max = status64.avail_max, + .overrange = status64.overrange, + .suspended_state = status64.suspended_state, + .audio_tstamp_data = status64.audio_tstamp_data, + .audio_tstamp_sec = status64.audio_tstamp_sec, + .audio_tstamp_nsec = status64.audio_tstamp_nsec, + .driver_tstamp_sec = status64.audio_tstamp_sec, + .driver_tstamp_nsec = status64.audio_tstamp_nsec, + .audio_tstamp_accuracy = status64.audio_tstamp_accuracy, + }; + + if (copy_to_user(_status, &status32, sizeof(status32))) + return -EFAULT; + + return 0; +} + static int snd_pcm_channel_info(struct snd_pcm_substream *substream, struct snd_pcm_channel_info * info) { @@ -2959,10 +3017,14 @@ static int snd_pcm_common_ioctl(struct file *file, return snd_pcm_hw_free(substream); case SNDRV_PCM_IOCTL_SW_PARAMS: return snd_pcm_sw_params_user(substream, arg); - case SNDRV_PCM_IOCTL_STATUS: - return snd_pcm_status_user(substream, arg, false); - case SNDRV_PCM_IOCTL_STATUS_EXT: - return snd_pcm_status_user(substream, arg, true); + case SNDRV_PCM_IOCTL_STATUS32: + return snd_pcm_status_user32(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT32: + return snd_pcm_status_user32(substream, arg, true); + case SNDRV_PCM_IOCTL_STATUS64: + return snd_pcm_status_user64(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT64: + return snd_pcm_status_user64(substream, arg, true); case SNDRV_PCM_IOCTL_CHANNEL_INFO: return snd_pcm_channel_info_user(substream, arg); case SNDRV_PCM_IOCTL_PREPARE: |