Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * compat ioctls for control API
4 : : *
5 : : * Copyright (c) by Takashi Iwai <tiwai@suse.de>
6 : : */
7 : :
8 : : /* this file included from control.c */
9 : :
10 : : #include <linux/compat.h>
11 : : #include <linux/slab.h>
12 : :
13 : : struct snd_ctl_elem_list32 {
14 : : u32 offset;
15 : : u32 space;
16 : : u32 used;
17 : : u32 count;
18 : : u32 pids;
19 : : unsigned char reserved[50];
20 : : } /* don't set packed attribute here */;
21 : :
22 : 0 : static int snd_ctl_elem_list_compat(struct snd_card *card,
23 : : struct snd_ctl_elem_list32 __user *data32)
24 : : {
25 : 0 : struct snd_ctl_elem_list __user *data;
26 : 0 : compat_caddr_t ptr;
27 : 0 : int err;
28 : :
29 : 0 : data = compat_alloc_user_space(sizeof(*data));
30 : :
31 : : /* offset, space, used, count */
32 [ # # # # ]: 0 : if (copy_in_user(data, data32, 4 * sizeof(u32)))
33 : 0 : return -EFAULT;
34 : : /* pids */
35 [ # # ]: 0 : if (get_user(ptr, &data32->pids) ||
36 [ # # ]: 0 : put_user(compat_ptr(ptr), &data->pids))
37 : : return -EFAULT;
38 : 0 : err = snd_ctl_elem_list(card, data);
39 [ # # ]: 0 : if (err < 0)
40 : : return err;
41 : : /* copy the result */
42 [ # # # # ]: 0 : if (copy_in_user(data32, data, 4 * sizeof(u32)))
43 : 0 : return -EFAULT;
44 : : return 0;
45 : : }
46 : :
47 : : /*
48 : : * control element info
49 : : * it uses union, so the things are not easy..
50 : : */
51 : :
52 : : struct snd_ctl_elem_info32 {
53 : : struct snd_ctl_elem_id id; // the size of struct is same
54 : : s32 type;
55 : : u32 access;
56 : : u32 count;
57 : : s32 owner;
58 : : union {
59 : : struct {
60 : : s32 min;
61 : : s32 max;
62 : : s32 step;
63 : : } integer;
64 : : struct {
65 : : u64 min;
66 : : u64 max;
67 : : u64 step;
68 : : } integer64;
69 : : struct {
70 : : u32 items;
71 : : u32 item;
72 : : char name[64];
73 : : u64 names_ptr;
74 : : u32 names_length;
75 : : } enumerated;
76 : : unsigned char reserved[128];
77 : : } value;
78 : : unsigned char reserved[64];
79 : : } __attribute__((packed));
80 : :
81 : 0 : static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
82 : : struct snd_ctl_elem_info32 __user *data32)
83 : : {
84 : 0 : struct snd_ctl_elem_info *data;
85 : 0 : int err;
86 : :
87 : 0 : data = kzalloc(sizeof(*data), GFP_KERNEL);
88 [ # # ]: 0 : if (! data)
89 : : return -ENOMEM;
90 : :
91 : 0 : err = -EFAULT;
92 : : /* copy id */
93 [ # # # # ]: 0 : if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
94 : 0 : goto error;
95 : : /* we need to copy the item index.
96 : : * hope this doesn't break anything..
97 : : */
98 [ # # ]: 0 : if (get_user(data->value.enumerated.item, &data32->value.enumerated.item))
99 : 0 : goto error;
100 : :
101 : 0 : err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
102 [ # # ]: 0 : if (err < 0)
103 : 0 : goto error;
104 : 0 : err = snd_ctl_elem_info(ctl, data);
105 [ # # ]: 0 : if (err < 0)
106 : 0 : goto error;
107 : : /* restore info to 32bit */
108 : 0 : err = -EFAULT;
109 : : /* id, type, access, count */
110 [ # # # # : 0 : if (copy_to_user(&data32->id, &data->id, sizeof(data->id)) ||
# # ]
111 [ # # ]: 0 : copy_to_user(&data32->type, &data->type, 3 * sizeof(u32)))
112 : 0 : goto error;
113 [ # # ]: 0 : if (put_user(data->owner, &data32->owner))
114 : 0 : goto error;
115 [ # # # # ]: 0 : switch (data->type) {
116 : : case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
117 : : case SNDRV_CTL_ELEM_TYPE_INTEGER:
118 [ # # ]: 0 : if (put_user(data->value.integer.min, &data32->value.integer.min) ||
119 [ # # ]: 0 : put_user(data->value.integer.max, &data32->value.integer.max) ||
120 [ # # ]: 0 : put_user(data->value.integer.step, &data32->value.integer.step))
121 : 0 : goto error;
122 : : break;
123 : 0 : case SNDRV_CTL_ELEM_TYPE_INTEGER64:
124 [ # # ]: 0 : if (copy_to_user(&data32->value.integer64,
125 [ # # ]: 0 : &data->value.integer64,
126 : : sizeof(data->value.integer64)))
127 : 0 : goto error;
128 : : break;
129 : 0 : case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
130 [ # # ]: 0 : if (copy_to_user(&data32->value.enumerated,
131 [ # # ]: 0 : &data->value.enumerated,
132 : : sizeof(data->value.enumerated)))
133 : 0 : goto error;
134 : : break;
135 : : default:
136 : : break;
137 : : }
138 : : err = 0;
139 : 0 : error:
140 : 0 : kfree(data);
141 : 0 : return err;
142 : : }
143 : :
144 : : /* read / write */
145 : : struct snd_ctl_elem_value32 {
146 : : struct snd_ctl_elem_id id;
147 : : unsigned int indirect; /* bit-field causes misalignment */
148 : : union {
149 : : s32 integer[128];
150 : : unsigned char data[512];
151 : : #ifndef CONFIG_X86_64
152 : : s64 integer64[64];
153 : : #endif
154 : : } value;
155 : : unsigned char reserved[128];
156 : : };
157 : :
158 : : #ifdef CONFIG_X86_X32
159 : : /* x32 has a different alignment for 64bit values from ia32 */
160 : : struct snd_ctl_elem_value_x32 {
161 : : struct snd_ctl_elem_id id;
162 : : unsigned int indirect; /* bit-field causes misalignment */
163 : : union {
164 : : s32 integer[128];
165 : : unsigned char data[512];
166 : : s64 integer64[64];
167 : : } value;
168 : : unsigned char reserved[128];
169 : : };
170 : : #endif /* CONFIG_X86_X32 */
171 : :
172 : : /* get the value type and count of the control */
173 : 0 : static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
174 : : int *countp)
175 : : {
176 : 0 : struct snd_kcontrol *kctl;
177 : 0 : struct snd_ctl_elem_info *info;
178 : 0 : int err;
179 : :
180 : 0 : down_read(&card->controls_rwsem);
181 : 0 : kctl = snd_ctl_find_id(card, id);
182 [ # # ]: 0 : if (! kctl) {
183 : 0 : up_read(&card->controls_rwsem);
184 : 0 : return -ENOENT;
185 : : }
186 : 0 : info = kzalloc(sizeof(*info), GFP_KERNEL);
187 [ # # ]: 0 : if (info == NULL) {
188 : 0 : up_read(&card->controls_rwsem);
189 : 0 : return -ENOMEM;
190 : : }
191 : 0 : info->id = *id;
192 : 0 : err = kctl->info(kctl, info);
193 : 0 : up_read(&card->controls_rwsem);
194 [ # # ]: 0 : if (err >= 0) {
195 : 0 : err = info->type;
196 : 0 : *countp = info->count;
197 : : }
198 : 0 : kfree(info);
199 : 0 : return err;
200 : : }
201 : :
202 : 0 : static int get_elem_size(int type, int count)
203 : : {
204 : 0 : switch (type) {
205 : 0 : case SNDRV_CTL_ELEM_TYPE_INTEGER64:
206 : 0 : return sizeof(s64) * count;
207 : 0 : case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
208 : 0 : return sizeof(int) * count;
209 : : case SNDRV_CTL_ELEM_TYPE_BYTES:
210 : : return 512;
211 : : case SNDRV_CTL_ELEM_TYPE_IEC958:
212 : : return sizeof(struct snd_aes_iec958);
213 : : default:
214 : : return -1;
215 : : }
216 : : }
217 : :
218 : 0 : static int copy_ctl_value_from_user(struct snd_card *card,
219 : : struct snd_ctl_elem_value *data,
220 : : void __user *userdata,
221 : : void __user *valuep,
222 : : int *typep, int *countp)
223 : : {
224 : 0 : struct snd_ctl_elem_value32 __user *data32 = userdata;
225 : 0 : int i, type, size;
226 : 0 : int uninitialized_var(count);
227 : 0 : unsigned int indirect;
228 : :
229 [ # # # # ]: 0 : if (copy_from_user(&data->id, &data32->id, sizeof(data->id)))
230 : 0 : return -EFAULT;
231 [ # # ]: 0 : if (get_user(indirect, &data32->indirect))
232 : : return -EFAULT;
233 [ # # ]: 0 : if (indirect)
234 : : return -EINVAL;
235 : 0 : type = get_ctl_type(card, &data->id, &count);
236 [ # # ]: 0 : if (type < 0)
237 : : return type;
238 : :
239 [ # # ]: 0 : if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
240 : : type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
241 [ # # ]: 0 : for (i = 0; i < count; i++) {
242 : 0 : s32 __user *intp = valuep;
243 : 0 : int val;
244 [ # # ]: 0 : if (get_user(val, &intp[i]))
245 : : return -EFAULT;
246 : 0 : data->value.integer.value[i] = val;
247 : : }
248 : : } else {
249 [ # # # # : 0 : size = get_elem_size(type, count);
# ]
250 [ # # ]: 0 : if (size < 0) {
251 : 0 : dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
252 : 0 : return -EINVAL;
253 : : }
254 [ # # # # ]: 0 : if (copy_from_user(data->value.bytes.data, valuep, size))
255 : : return -EFAULT;
256 : : }
257 : :
258 : 0 : *typep = type;
259 : 0 : *countp = count;
260 : 0 : return 0;
261 : : }
262 : :
263 : : /* restore the value to 32bit */
264 : : static int copy_ctl_value_to_user(void __user *userdata,
265 : : void __user *valuep,
266 : : struct snd_ctl_elem_value *data,
267 : : int type, int count)
268 : : {
269 : : int i, size;
270 : :
271 : : if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
272 : : type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
273 : : for (i = 0; i < count; i++) {
274 : : s32 __user *intp = valuep;
275 : : int val;
276 : : val = data->value.integer.value[i];
277 : : if (put_user(val, &intp[i]))
278 : : return -EFAULT;
279 : : }
280 : : } else {
281 : : size = get_elem_size(type, count);
282 : : if (copy_to_user(valuep, data->value.bytes.data, size))
283 : : return -EFAULT;
284 : : }
285 : : return 0;
286 : : }
287 : :
288 : 0 : static int ctl_elem_read_user(struct snd_card *card,
289 : : void __user *userdata, void __user *valuep)
290 : : {
291 : 0 : struct snd_ctl_elem_value *data;
292 : 0 : int err, type, count;
293 : :
294 : 0 : data = kzalloc(sizeof(*data), GFP_KERNEL);
295 [ # # ]: 0 : if (data == NULL)
296 : : return -ENOMEM;
297 : :
298 : 0 : err = copy_ctl_value_from_user(card, data, userdata, valuep,
299 : : &type, &count);
300 [ # # ]: 0 : if (err < 0)
301 : 0 : goto error;
302 : :
303 : 0 : err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
304 [ # # ]: 0 : if (err < 0)
305 : 0 : goto error;
306 : 0 : err = snd_ctl_elem_read(card, data);
307 [ # # ]: 0 : if (err < 0)
308 : 0 : goto error;
309 : 0 : err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
310 : 0 : error:
311 : 0 : kfree(data);
312 : 0 : return err;
313 : : }
314 : :
315 : 0 : static int ctl_elem_write_user(struct snd_ctl_file *file,
316 : : void __user *userdata, void __user *valuep)
317 : : {
318 : 0 : struct snd_ctl_elem_value *data;
319 : 0 : struct snd_card *card = file->card;
320 : 0 : int err, type, count;
321 : :
322 : 0 : data = kzalloc(sizeof(*data), GFP_KERNEL);
323 [ # # ]: 0 : if (data == NULL)
324 : : return -ENOMEM;
325 : :
326 : 0 : err = copy_ctl_value_from_user(card, data, userdata, valuep,
327 : : &type, &count);
328 [ # # ]: 0 : if (err < 0)
329 : 0 : goto error;
330 : :
331 : 0 : err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
332 [ # # ]: 0 : if (err < 0)
333 : 0 : goto error;
334 : 0 : err = snd_ctl_elem_write(card, file, data);
335 [ # # ]: 0 : if (err < 0)
336 : 0 : goto error;
337 : 0 : err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
338 : 0 : error:
339 : 0 : kfree(data);
340 : 0 : return err;
341 : : }
342 : :
343 : 0 : static int snd_ctl_elem_read_user_compat(struct snd_card *card,
344 : : struct snd_ctl_elem_value32 __user *data32)
345 : : {
346 : 0 : return ctl_elem_read_user(card, data32, &data32->value);
347 : : }
348 : :
349 : 0 : static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
350 : : struct snd_ctl_elem_value32 __user *data32)
351 : : {
352 : 0 : return ctl_elem_write_user(file, data32, &data32->value);
353 : : }
354 : :
355 : : #ifdef CONFIG_X86_X32
356 : : static int snd_ctl_elem_read_user_x32(struct snd_card *card,
357 : : struct snd_ctl_elem_value_x32 __user *data32)
358 : : {
359 : : return ctl_elem_read_user(card, data32, &data32->value);
360 : : }
361 : :
362 : : static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
363 : : struct snd_ctl_elem_value_x32 __user *data32)
364 : : {
365 : : return ctl_elem_write_user(file, data32, &data32->value);
366 : : }
367 : : #endif /* CONFIG_X86_X32 */
368 : :
369 : : /* add or replace a user control */
370 : 0 : static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
371 : : struct snd_ctl_elem_info32 __user *data32,
372 : : int replace)
373 : : {
374 : 0 : struct snd_ctl_elem_info *data;
375 : 0 : int err;
376 : :
377 : 0 : data = kzalloc(sizeof(*data), GFP_KERNEL);
378 [ # # ]: 0 : if (! data)
379 : : return -ENOMEM;
380 : :
381 : 0 : err = -EFAULT;
382 : : /* id, type, access, count */ \
383 [ # # # # : 0 : if (copy_from_user(&data->id, &data32->id, sizeof(data->id)) ||
# # ]
384 [ # # ]: 0 : copy_from_user(&data->type, &data32->type, 3 * sizeof(u32)))
385 : 0 : goto error;
386 [ # # ]: 0 : if (get_user(data->owner, &data32->owner))
387 : 0 : goto error;
388 [ # # # # ]: 0 : switch (data->type) {
389 : : case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
390 : : case SNDRV_CTL_ELEM_TYPE_INTEGER:
391 [ # # ]: 0 : if (get_user(data->value.integer.min, &data32->value.integer.min) ||
392 [ # # ]: 0 : get_user(data->value.integer.max, &data32->value.integer.max) ||
393 [ # # ]: 0 : get_user(data->value.integer.step, &data32->value.integer.step))
394 : 0 : goto error;
395 : : break;
396 : 0 : case SNDRV_CTL_ELEM_TYPE_INTEGER64:
397 [ # # ]: 0 : if (copy_from_user(&data->value.integer64,
398 [ # # ]: 0 : &data32->value.integer64,
399 : : sizeof(data->value.integer64)))
400 : 0 : goto error;
401 : : break;
402 : 0 : case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
403 [ # # ]: 0 : if (copy_from_user(&data->value.enumerated,
404 [ # # ]: 0 : &data32->value.enumerated,
405 : : sizeof(data->value.enumerated)))
406 : 0 : goto error;
407 : 0 : data->value.enumerated.names_ptr =
408 : 0 : (uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
409 : 0 : break;
410 : : default:
411 : : break;
412 : : }
413 : 0 : err = snd_ctl_elem_add(file, data, replace);
414 : 0 : error:
415 : 0 : kfree(data);
416 : 0 : return err;
417 : : }
418 : :
419 : : enum {
420 : : SNDRV_CTL_IOCTL_ELEM_LIST32 = _IOWR('U', 0x10, struct snd_ctl_elem_list32),
421 : : SNDRV_CTL_IOCTL_ELEM_INFO32 = _IOWR('U', 0x11, struct snd_ctl_elem_info32),
422 : : SNDRV_CTL_IOCTL_ELEM_READ32 = _IOWR('U', 0x12, struct snd_ctl_elem_value32),
423 : : SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
424 : : SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
425 : : SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
426 : : #ifdef CONFIG_X86_X32
427 : : SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
428 : : SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
429 : : #endif /* CONFIG_X86_X32 */
430 : : };
431 : :
432 : 0 : static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
433 : : {
434 : 0 : struct snd_ctl_file *ctl;
435 : 0 : struct snd_kctl_ioctl *p;
436 [ # # ]: 0 : void __user *argp = compat_ptr(arg);
437 : 0 : int err;
438 : :
439 : 0 : ctl = file->private_data;
440 [ # # # # : 0 : if (snd_BUG_ON(!ctl || !ctl->card))
# # ]
441 : : return -ENXIO;
442 : :
443 [ # # # # : 0 : switch (cmd) {
# # # # ]
444 : 0 : case SNDRV_CTL_IOCTL_PVERSION:
445 : : case SNDRV_CTL_IOCTL_CARD_INFO:
446 : : case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
447 : : case SNDRV_CTL_IOCTL_POWER:
448 : : case SNDRV_CTL_IOCTL_POWER_STATE:
449 : : case SNDRV_CTL_IOCTL_ELEM_LOCK:
450 : : case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
451 : : case SNDRV_CTL_IOCTL_ELEM_REMOVE:
452 : : case SNDRV_CTL_IOCTL_TLV_READ:
453 : : case SNDRV_CTL_IOCTL_TLV_WRITE:
454 : : case SNDRV_CTL_IOCTL_TLV_COMMAND:
455 : 0 : return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
456 : 0 : case SNDRV_CTL_IOCTL_ELEM_LIST32:
457 : 0 : return snd_ctl_elem_list_compat(ctl->card, argp);
458 : 0 : case SNDRV_CTL_IOCTL_ELEM_INFO32:
459 : 0 : return snd_ctl_elem_info_compat(ctl, argp);
460 : 0 : case SNDRV_CTL_IOCTL_ELEM_READ32:
461 : 0 : return snd_ctl_elem_read_user_compat(ctl->card, argp);
462 : : case SNDRV_CTL_IOCTL_ELEM_WRITE32:
463 : 0 : return snd_ctl_elem_write_user_compat(ctl, argp);
464 : 0 : case SNDRV_CTL_IOCTL_ELEM_ADD32:
465 : 0 : return snd_ctl_elem_add_compat(ctl, argp, 0);
466 : 0 : case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
467 : 0 : return snd_ctl_elem_add_compat(ctl, argp, 1);
468 : : #ifdef CONFIG_X86_X32
469 : : case SNDRV_CTL_IOCTL_ELEM_READ_X32:
470 : : return snd_ctl_elem_read_user_x32(ctl->card, argp);
471 : : case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
472 : : return snd_ctl_elem_write_user_x32(ctl, argp);
473 : : #endif /* CONFIG_X86_X32 */
474 : : }
475 : :
476 : 0 : down_read(&snd_ioctl_rwsem);
477 [ # # ]: 0 : list_for_each_entry(p, &snd_control_compat_ioctls, list) {
478 [ # # ]: 0 : if (p->fioctl) {
479 : 0 : err = p->fioctl(ctl->card, ctl, cmd, arg);
480 [ # # ]: 0 : if (err != -ENOIOCTLCMD) {
481 : 0 : up_read(&snd_ioctl_rwsem);
482 : 0 : return err;
483 : : }
484 : : }
485 : : }
486 : 0 : up_read(&snd_ioctl_rwsem);
487 : 0 : return -ENOIOCTLCMD;
488 : : }
|