ALSA: timer: fix gparams ioctl compatibility for different architectures
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 22 Mar 2016 23:03:59 +0000 (08:03 +0900)
committerTakashi Iwai <tiwai@suse.de>
Wed, 23 Mar 2016 07:06:16 +0000 (08:06 +0100)
'struct snd_timer_gparams' includes some members with 'unsigned long',
therefore its size differs depending on data models of architecture. As
a result, x86/x32 applications fail to execute ioctl(2) with
SNDRV_TIMER_GPARAMS command on x86_64 machine.

This commit fixes this bug by adding a pair of structure and ioctl
command for the compatibility.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/core/timer.c
sound/core/timer_compat.c

index aa1b15c..ea4d999 100644 (file)
@@ -1502,17 +1502,13 @@ static int snd_timer_user_ginfo(struct file *file,
        return err;
 }
 
-static int snd_timer_user_gparams(struct file *file,
-                                 struct snd_timer_gparams __user *_gparams)
+static int timer_set_gparams(struct snd_timer_gparams *gparams)
 {
-       struct snd_timer_gparams gparams;
        struct snd_timer *t;
        int err;
 
-       if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
-               return -EFAULT;
        mutex_lock(&register_mutex);
-       t = snd_timer_find(&gparams.tid);
+       t = snd_timer_find(&gparams->tid);
        if (!t) {
                err = -ENODEV;
                goto _error;
@@ -1525,12 +1521,22 @@ static int snd_timer_user_gparams(struct file *file,
                err = -ENOSYS;
                goto _error;
        }
-       err = t->hw.set_period(t, gparams.period_num, gparams.period_den);
+       err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
 _error:
        mutex_unlock(&register_mutex);
        return err;
 }
 
+static int snd_timer_user_gparams(struct file *file,
+                                 struct snd_timer_gparams __user *_gparams)
+{
+       struct snd_timer_gparams gparams;
+
+       if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
+               return -EFAULT;
+       return timer_set_gparams(&gparams);
+}
+
 static int snd_timer_user_gstatus(struct file *file,
                                  struct snd_timer_gstatus __user *_gstatus)
 {
index 2e90822..6a437eb 100644 (file)
 
 #include <linux/compat.h>
 
+/*
+ * ILP32/LP64 has different size for 'long' type. Additionally, the size
+ * of storage alignment differs depending on architectures. Here, '__packed'
+ * qualifier is used so that the size of this structure is multiple of 4 and
+ * it fits to any architectures with 32 bit storage alignment.
+ */
+struct snd_timer_gparams32 {
+       struct snd_timer_id tid;
+       u32 period_num;
+       u32 period_den;
+       unsigned char reserved[32];
+} __packed;
+
 struct snd_timer_info32 {
        u32 flags;
        s32 card;
@@ -32,6 +45,19 @@ struct snd_timer_info32 {
        unsigned char reserved[64];
 };
 
+static int snd_timer_user_gparams_compat(struct file *file,
+                                       struct snd_timer_gparams32 __user *user)
+{
+       struct snd_timer_gparams gparams;
+
+       if (copy_from_user(&gparams.tid, &user->tid, sizeof(gparams.tid)) ||
+           get_user(gparams.period_num, &user->period_num) ||
+           get_user(gparams.period_den, &user->period_den))
+               return -EFAULT;
+
+       return timer_set_gparams(&gparams);
+}
+
 static int snd_timer_user_info_compat(struct file *file,
                                      struct snd_timer_info32 __user *_info)
 {
@@ -99,6 +125,7 @@ static int snd_timer_user_status_compat(struct file *file,
  */
 
 enum {
+       SNDRV_TIMER_IOCTL_GPARAMS32 = _IOW('T', 0x04, struct snd_timer_gparams32),
        SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
        SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32),
 #ifdef CONFIG_X86_X32
@@ -114,7 +141,6 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
        case SNDRV_TIMER_IOCTL_PVERSION:
        case SNDRV_TIMER_IOCTL_TREAD:
        case SNDRV_TIMER_IOCTL_GINFO:
-       case SNDRV_TIMER_IOCTL_GPARAMS:
        case SNDRV_TIMER_IOCTL_GSTATUS:
        case SNDRV_TIMER_IOCTL_SELECT:
        case SNDRV_TIMER_IOCTL_PARAMS:
@@ -128,6 +154,8 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
        case SNDRV_TIMER_IOCTL_PAUSE_OLD:
        case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
                return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
+       case SNDRV_TIMER_IOCTL_GPARAMS32:
+               return snd_timer_user_gparams_compat(file, argp);
        case SNDRV_TIMER_IOCTL_INFO32:
                return snd_timer_user_info_compat(file, argp);
        case SNDRV_TIMER_IOCTL_STATUS32: