brintos

brintos / linux-shallow public Read only

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