diff options
author | Takashi Iwai <tiwai@suse.de> | 2016-02-28 05:23:09 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2016-02-28 11:44:35 -0500 |
commit | 513ace79b657e2022a592e77f24074e088681ecc (patch) | |
tree | 88e700a897d8c83465c6d2d4ce8975af15993cfe | |
parent | 6236d8bb2afcfe71b88ecea554e0dc638090a45f (diff) |
ALSA: pcm: Fix ioctls for X32 ABI
X32 ABI uses the 64bit timespec in addition to 64bit alignment of
64bit values. This leads to incompatibilities in some PCM ioctls
involved with snd_pcm_channel_info, snd_pcm_status and
snd_pcm_sync_ptr structs. Fix the PCM compat ABI for these ioctls
like the previous commit for ctl API.
Reported-by: Steven Newbury <steve@snewbury.org.uk>
Cc: <stable@vger.kernel.org> # v3.4+
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/core/pcm_compat.c | 177 |
1 files changed, 176 insertions, 1 deletions
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 9630e9f72b7b..1f64ab0c2a95 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c | |||
@@ -183,6 +183,14 @@ static int snd_pcm_ioctl_channel_info_compat(struct snd_pcm_substream *substream | |||
183 | return err; | 183 | return err; |
184 | } | 184 | } |
185 | 185 | ||
186 | #ifdef CONFIG_X86_X32 | ||
187 | /* X32 ABI has the same struct as x86-64 for snd_pcm_channel_info */ | ||
188 | static int snd_pcm_channel_info_user(struct snd_pcm_substream *substream, | ||
189 | struct snd_pcm_channel_info __user *src); | ||
190 | #define snd_pcm_ioctl_channel_info_x32(s, p) \ | ||
191 | snd_pcm_channel_info_user(s, p) | ||
192 | #endif /* CONFIG_X86_X32 */ | ||
193 | |||
186 | struct snd_pcm_status32 { | 194 | struct snd_pcm_status32 { |
187 | s32 state; | 195 | s32 state; |
188 | struct compat_timespec trigger_tstamp; | 196 | struct compat_timespec trigger_tstamp; |
@@ -243,6 +251,71 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, | |||
243 | return err; | 251 | return err; |
244 | } | 252 | } |
245 | 253 | ||
254 | #ifdef CONFIG_X86_X32 | ||
255 | /* X32 ABI has 64bit timespec and 64bit alignment */ | ||
256 | struct snd_pcm_status_x32 { | ||
257 | s32 state; | ||
258 | u32 rsvd; /* alignment */ | ||
259 | struct timespec trigger_tstamp; | ||
260 | struct timespec tstamp; | ||
261 | u32 appl_ptr; | ||
262 | u32 hw_ptr; | ||
263 | s32 delay; | ||
264 | u32 avail; | ||
265 | u32 avail_max; | ||
266 | u32 overrange; | ||
267 | s32 suspended_state; | ||
268 | u32 audio_tstamp_data; | ||
269 | struct timespec audio_tstamp; | ||
270 | struct timespec driver_tstamp; | ||
271 | u32 audio_tstamp_accuracy; | ||
272 | unsigned char reserved[52-2*sizeof(struct timespec)]; | ||
273 | } __packed; | ||
274 | |||
275 | #define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst)) | ||
276 | |||
277 | static int snd_pcm_status_user_x32(struct snd_pcm_substream *substream, | ||
278 | struct snd_pcm_status_x32 __user *src, | ||
279 | bool ext) | ||
280 | { | ||
281 | struct snd_pcm_status status; | ||
282 | int err; | ||
283 | |||
284 | memset(&status, 0, sizeof(status)); | ||
285 | /* | ||
286 | * with extension, parameters are read/write, | ||
287 | * get audio_tstamp_data from user, | ||
288 | * ignore rest of status structure | ||
289 | */ | ||
290 | if (ext && get_user(status.audio_tstamp_data, | ||
291 | (u32 __user *)(&src->audio_tstamp_data))) | ||
292 | return -EFAULT; | ||
293 | err = snd_pcm_status(substream, &status); | ||
294 | if (err < 0) | ||
295 | return err; | ||
296 | |||
297 | if (clear_user(src, sizeof(*src))) | ||
298 | return -EFAULT; | ||
299 | if (put_user(status.state, &src->state) || | ||
300 | put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) || | ||
301 | put_timespec(&status.tstamp, &src->tstamp) || | ||
302 | put_user(status.appl_ptr, &src->appl_ptr) || | ||
303 | put_user(status.hw_ptr, &src->hw_ptr) || | ||
304 | put_user(status.delay, &src->delay) || | ||
305 | put_user(status.avail, &src->avail) || | ||
306 | put_user(status.avail_max, &src->avail_max) || | ||
307 | put_user(status.overrange, &src->overrange) || | ||
308 | put_user(status.suspended_state, &src->suspended_state) || | ||
309 | put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || | ||
310 | put_timespec(&status.audio_tstamp, &src->audio_tstamp) || | ||
311 | put_timespec(&status.driver_tstamp, &src->driver_tstamp) || | ||
312 | put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) | ||
313 | return -EFAULT; | ||
314 | |||
315 | return err; | ||
316 | } | ||
317 | #endif /* CONFIG_X86_X32 */ | ||
318 | |||
246 | /* both for HW_PARAMS and HW_REFINE */ | 319 | /* both for HW_PARAMS and HW_REFINE */ |
247 | static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, | 320 | static int snd_pcm_ioctl_hw_params_compat(struct snd_pcm_substream *substream, |
248 | int refine, | 321 | int refine, |
@@ -469,6 +542,93 @@ static int snd_pcm_ioctl_sync_ptr_compat(struct snd_pcm_substream *substream, | |||
469 | return 0; | 542 | return 0; |
470 | } | 543 | } |
471 | 544 | ||
545 | #ifdef CONFIG_X86_X32 | ||
546 | /* X32 ABI has 64bit timespec and 64bit alignment */ | ||
547 | struct snd_pcm_mmap_status_x32 { | ||
548 | s32 state; | ||
549 | s32 pad1; | ||
550 | u32 hw_ptr; | ||
551 | u32 pad2; /* alignment */ | ||
552 | struct timespec tstamp; | ||
553 | s32 suspended_state; | ||
554 | struct timespec audio_tstamp; | ||
555 | } __packed; | ||
556 | |||
557 | struct snd_pcm_mmap_control_x32 { | ||
558 | u32 appl_ptr; | ||
559 | u32 avail_min; | ||
560 | }; | ||
561 | |||
562 | struct snd_pcm_sync_ptr_x32 { | ||
563 | u32 flags; | ||
564 | u32 rsvd; /* alignment */ | ||
565 | union { | ||
566 | struct snd_pcm_mmap_status_x32 status; | ||
567 | unsigned char reserved[64]; | ||
568 | } s; | ||
569 | union { | ||
570 | struct snd_pcm_mmap_control_x32 control; | ||
571 | unsigned char reserved[64]; | ||
572 | } c; | ||
573 | } __packed; | ||
574 | |||
575 | static int snd_pcm_ioctl_sync_ptr_x32(struct snd_pcm_substream *substream, | ||
576 | struct snd_pcm_sync_ptr_x32 __user *src) | ||
577 | { | ||
578 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
579 | volatile struct snd_pcm_mmap_status *status; | ||
580 | volatile struct snd_pcm_mmap_control *control; | ||
581 | u32 sflags; | ||
582 | struct snd_pcm_mmap_control scontrol; | ||
583 | struct snd_pcm_mmap_status sstatus; | ||
584 | snd_pcm_uframes_t boundary; | ||
585 | int err; | ||
586 | |||
587 | if (snd_BUG_ON(!runtime)) | ||
588 | return -EINVAL; | ||
589 | |||
590 | if (get_user(sflags, &src->flags) || | ||
591 | get_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || | ||
592 | get_user(scontrol.avail_min, &src->c.control.avail_min)) | ||
593 | return -EFAULT; | ||
594 | if (sflags & SNDRV_PCM_SYNC_PTR_HWSYNC) { | ||
595 | err = snd_pcm_hwsync(substream); | ||
596 | if (err < 0) | ||
597 | return err; | ||
598 | } | ||
599 | status = runtime->status; | ||
600 | control = runtime->control; | ||
601 | boundary = recalculate_boundary(runtime); | ||
602 | if (!boundary) | ||
603 | boundary = 0x7fffffff; | ||
604 | snd_pcm_stream_lock_irq(substream); | ||
605 | /* FIXME: we should consider the boundary for the sync from app */ | ||
606 | if (!(sflags & SNDRV_PCM_SYNC_PTR_APPL)) | ||
607 | control->appl_ptr = scontrol.appl_ptr; | ||
608 | else | ||
609 | scontrol.appl_ptr = control->appl_ptr % boundary; | ||
610 | if (!(sflags & SNDRV_PCM_SYNC_PTR_AVAIL_MIN)) | ||
611 | control->avail_min = scontrol.avail_min; | ||
612 | else | ||
613 | scontrol.avail_min = control->avail_min; | ||
614 | sstatus.state = status->state; | ||
615 | sstatus.hw_ptr = status->hw_ptr % boundary; | ||
616 | sstatus.tstamp = status->tstamp; | ||
617 | sstatus.suspended_state = status->suspended_state; | ||
618 | sstatus.audio_tstamp = status->audio_tstamp; | ||
619 | snd_pcm_stream_unlock_irq(substream); | ||
620 | if (put_user(sstatus.state, &src->s.status.state) || | ||
621 | put_user(sstatus.hw_ptr, &src->s.status.hw_ptr) || | ||
622 | put_timespec(&sstatus.tstamp, &src->s.status.tstamp) || | ||
623 | put_user(sstatus.suspended_state, &src->s.status.suspended_state) || | ||
624 | put_timespec(&sstatus.audio_tstamp, &src->s.status.audio_tstamp) || | ||
625 | put_user(scontrol.appl_ptr, &src->c.control.appl_ptr) || | ||
626 | put_user(scontrol.avail_min, &src->c.control.avail_min)) | ||
627 | return -EFAULT; | ||
628 | |||
629 | return 0; | ||
630 | } | ||
631 | #endif /* CONFIG_X86_X32 */ | ||
472 | 632 | ||
473 | /* | 633 | /* |
474 | */ | 634 | */ |
@@ -487,7 +647,12 @@ enum { | |||
487 | SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), | 647 | SNDRV_PCM_IOCTL_WRITEN_FRAMES32 = _IOW('A', 0x52, struct snd_xfern32), |
488 | SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32), | 648 | SNDRV_PCM_IOCTL_READN_FRAMES32 = _IOR('A', 0x53, struct snd_xfern32), |
489 | SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32), | 649 | SNDRV_PCM_IOCTL_SYNC_PTR32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr32), |
490 | 650 | #ifdef CONFIG_X86_X32 | |
651 | SNDRV_PCM_IOCTL_CHANNEL_INFO_X32 = _IOR('A', 0x32, struct snd_pcm_channel_info), | ||
652 | SNDRV_PCM_IOCTL_STATUS_X32 = _IOR('A', 0x20, struct snd_pcm_status_x32), | ||
653 | SNDRV_PCM_IOCTL_STATUS_EXT_X32 = _IOWR('A', 0x24, struct snd_pcm_status_x32), | ||
654 | SNDRV_PCM_IOCTL_SYNC_PTR_X32 = _IOWR('A', 0x23, struct snd_pcm_sync_ptr_x32), | ||
655 | #endif /* CONFIG_X86_X32 */ | ||
491 | }; | 656 | }; |
492 | 657 | ||
493 | static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) | 658 | static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) |
@@ -559,6 +724,16 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l | |||
559 | return snd_pcm_ioctl_rewind_compat(substream, argp); | 724 | return snd_pcm_ioctl_rewind_compat(substream, argp); |
560 | case SNDRV_PCM_IOCTL_FORWARD32: | 725 | case SNDRV_PCM_IOCTL_FORWARD32: |
561 | return snd_pcm_ioctl_forward_compat(substream, argp); | 726 | return snd_pcm_ioctl_forward_compat(substream, argp); |
727 | #ifdef CONFIG_X86_X32 | ||
728 | case SNDRV_PCM_IOCTL_STATUS_X32: | ||
729 | return snd_pcm_status_user_x32(substream, argp, false); | ||
730 | case SNDRV_PCM_IOCTL_STATUS_EXT_X32: | ||
731 | return snd_pcm_status_user_x32(substream, argp, true); | ||
732 | case SNDRV_PCM_IOCTL_SYNC_PTR_X32: | ||
733 | return snd_pcm_ioctl_sync_ptr_x32(substream, argp); | ||
734 | case SNDRV_PCM_IOCTL_CHANNEL_INFO_X32: | ||
735 | return snd_pcm_ioctl_channel_info_x32(substream, argp); | ||
736 | #endif /* CONFIG_X86_X32 */ | ||
562 | } | 737 | } |
563 | 738 | ||
564 | return -ENOIOCTLCMD; | 739 | return -ENOIOCTLCMD; |