Commit 6236d8bb authored by Takashi Iwai's avatar Takashi Iwai

ALSA: ctl: Fix ioctls for X32 ABI

The X32 ABI takes the same alignment like x86-64, and this may result
in the incompatible struct size from ia32.  Unfortunately, we hit this
in some control ABI: struct snd_ctl_elem_value differs between them
due to the position of 64bit variable array.  This ends up with the
unknown ioctl (ENOTTY) error.

The fix is to add the compat entries for the new aligned struct.
Reported-and-tested-by: default avatarSteven Newbury <steve@snewbury.org.uk>
Cc: <stable@vger.kernel.org> # v3.4+
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 473f4145
...@@ -170,6 +170,19 @@ struct snd_ctl_elem_value32 { ...@@ -170,6 +170,19 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128]; unsigned char reserved[128];
}; };
#ifdef CONFIG_X86_X32
/* x32 has a different alignment for 64bit values from ia32 */
struct snd_ctl_elem_value_x32 {
struct snd_ctl_elem_id id;
unsigned int indirect; /* bit-field causes misalignment */
union {
s32 integer[128];
unsigned char data[512];
s64 integer64[64];
} value;
unsigned char reserved[128];
};
#endif /* CONFIG_X86_X32 */
/* get the value type and count of the control */ /* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id, static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
...@@ -219,9 +232,11 @@ static int get_elem_size(int type, int count) ...@@ -219,9 +232,11 @@ static int get_elem_size(int type, int count)
static int copy_ctl_value_from_user(struct snd_card *card, static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value *data, struct snd_ctl_elem_value *data,
struct snd_ctl_elem_value32 __user *data32, void __user *userdata,
void __user *valuep,
int *typep, int *countp) int *typep, int *countp)
{ {
struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, type, size; int i, type, size;
int uninitialized_var(count); int uninitialized_var(count);
unsigned int indirect; unsigned int indirect;
...@@ -239,8 +254,9 @@ static int copy_ctl_value_from_user(struct snd_card *card, ...@@ -239,8 +254,9 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) { type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
s32 __user *intp = valuep;
int val; int val;
if (get_user(val, &data32->value.integer[i])) if (get_user(val, &intp[i]))
return -EFAULT; return -EFAULT;
data->value.integer.value[i] = val; data->value.integer.value[i] = val;
} }
...@@ -250,8 +266,7 @@ static int copy_ctl_value_from_user(struct snd_card *card, ...@@ -250,8 +266,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type); dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL; return -EINVAL;
} }
if (copy_from_user(data->value.bytes.data, if (copy_from_user(data->value.bytes.data, valuep, size))
data32->value.data, size))
return -EFAULT; return -EFAULT;
} }
...@@ -261,7 +276,8 @@ static int copy_ctl_value_from_user(struct snd_card *card, ...@@ -261,7 +276,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
} }
/* restore the value to 32bit */ /* restore the value to 32bit */
static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32, static int copy_ctl_value_to_user(void __user *userdata,
void __user *valuep,
struct snd_ctl_elem_value *data, struct snd_ctl_elem_value *data,
int type, int count) int type, int count)
{ {
...@@ -270,22 +286,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32, ...@@ -270,22 +286,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN || if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) { type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
s32 __user *intp = valuep;
int val; int val;
val = data->value.integer.value[i]; val = data->value.integer.value[i];
if (put_user(val, &data32->value.integer[i])) if (put_user(val, &intp[i]))
return -EFAULT; return -EFAULT;
} }
} else { } else {
size = get_elem_size(type, count); size = get_elem_size(type, count);
if (copy_to_user(data32->value.data, if (copy_to_user(valuep, data->value.bytes.data, size))
data->value.bytes.data, size))
return -EFAULT; return -EFAULT;
} }
return 0; return 0;
} }
static int snd_ctl_elem_read_user_compat(struct snd_card *card, static int ctl_elem_read_user(struct snd_card *card,
struct snd_ctl_elem_value32 __user *data32) void __user *userdata, void __user *valuep)
{ {
struct snd_ctl_elem_value *data; struct snd_ctl_elem_value *data;
int err, type, count; int err, type, count;
...@@ -294,7 +310,9 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card, ...@@ -294,7 +310,9 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
goto error; goto error;
snd_power_lock(card); snd_power_lock(card);
...@@ -303,14 +321,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card, ...@@ -303,14 +321,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
err = snd_ctl_elem_read(card, data); err = snd_ctl_elem_read(card, data);
snd_power_unlock(card); snd_power_unlock(card);
if (err >= 0) if (err >= 0)
err = copy_ctl_value_to_user(data32, data, type, count); err = copy_ctl_value_to_user(userdata, valuep, data,
type, count);
error: error:
kfree(data); kfree(data);
return err; return err;
} }
static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, static int ctl_elem_write_user(struct snd_ctl_file *file,
struct snd_ctl_elem_value32 __user *data32) void __user *userdata, void __user *valuep)
{ {
struct snd_ctl_elem_value *data; struct snd_ctl_elem_value *data;
struct snd_card *card = file->card; struct snd_card *card = file->card;
...@@ -320,7 +339,9 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, ...@@ -320,7 +339,9 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
if (data == NULL) if (data == NULL)
return -ENOMEM; return -ENOMEM;
if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) err = copy_ctl_value_from_user(card, data, userdata, valuep,
&type, &count);
if (err < 0)
goto error; goto error;
snd_power_lock(card); snd_power_lock(card);
...@@ -329,12 +350,39 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file, ...@@ -329,12 +350,39 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
err = snd_ctl_elem_write(card, file, data); err = snd_ctl_elem_write(card, file, data);
snd_power_unlock(card); snd_power_unlock(card);
if (err >= 0) if (err >= 0)
err = copy_ctl_value_to_user(data32, data, type, count); err = copy_ctl_value_to_user(userdata, valuep, data,
type, count);
error: error:
kfree(data); kfree(data);
return err; return err;
} }
static int snd_ctl_elem_read_user_compat(struct snd_card *card,
struct snd_ctl_elem_value32 __user *data32)
{
return ctl_elem_read_user(card, data32, &data32->value);
}
static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_value32 __user *data32)
{
return ctl_elem_write_user(file, data32, &data32->value);
}
#ifdef CONFIG_X86_X32
static int snd_ctl_elem_read_user_x32(struct snd_card *card,
struct snd_ctl_elem_value_x32 __user *data32)
{
return ctl_elem_read_user(card, data32, &data32->value);
}
static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
struct snd_ctl_elem_value_x32 __user *data32)
{
return ctl_elem_write_user(file, data32, &data32->value);
}
#endif /* CONFIG_X86_X32 */
/* add or replace a user control */ /* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file, static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32, struct snd_ctl_elem_info32 __user *data32,
...@@ -393,6 +441,10 @@ enum { ...@@ -393,6 +441,10 @@ enum {
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32), SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32), SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32), SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
#ifdef CONFIG_X86_X32
SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
#endif /* CONFIG_X86_X32 */
}; };
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg) static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
...@@ -431,6 +483,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns ...@@ -431,6 +483,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_ctl_elem_add_compat(ctl, argp, 0); return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32: case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1); return snd_ctl_elem_add_compat(ctl, argp, 1);
#ifdef CONFIG_X86_X32
case SNDRV_CTL_IOCTL_ELEM_READ_X32:
return snd_ctl_elem_read_user_x32(ctl->card, argp);
case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
return snd_ctl_elem_write_user_x32(ctl, argp);
#endif /* CONFIG_X86_X32 */
} }
down_read(&snd_ioctl_rwsem); down_read(&snd_ioctl_rwsem);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment