diff options
Diffstat (limited to 'sound/soc/sof/control.c')
-rw-r--r-- | sound/soc/sof/control.c | 149 |
1 files changed, 118 insertions, 31 deletions
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 186eea105bb1..0352d2b61358 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -221,7 +221,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; size_t size; - int ret = 0; if (be->max > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -230,20 +229,20 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, return -EINVAL; } - size = data->size + sizeof(*data); - if (size > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > be->max - sizeof(*data)) { dev_err_ratelimited(scomp->dev, - "error: DSP sent %zu bytes max is %d\n", - size, be->max); - ret = -EINVAL; - goto out; + "error: %u bytes of control data is invalid, max is %zu\n", + data->size, be->max - sizeof(*data)); + return -EINVAL; } + size = data->size + sizeof(*data); + /* copy back to kcontrol */ memcpy(ucontrol->value.bytes.data, data, size); -out: - return ret; + return 0; } int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, @@ -255,7 +254,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; - size_t size = data->size + sizeof(*data); + size_t size; if (be->max > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -264,13 +263,16 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - if (size > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > be->max - sizeof(*data)) { dev_err_ratelimited(scomp->dev, - "error: size too big %zu bytes max is %d\n", - size, be->max); + "error: data size too big %u bytes max is %zu\n", + data->size, be->max - sizeof(*data)); return -EINVAL; } + size = data->size + sizeof(*data); + /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); @@ -298,6 +300,10 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; + /* make sure we have at least a header */ + if (size < sizeof(struct snd_ctl_tlv)) + return -EINVAL; + /* * The beginning of bytes data contains a header from where * the length (as bytes) is needed to know the correct copy @@ -306,6 +312,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv))) return -EFAULT; + /* make sure TLV info is consistent */ + if (header.length + sizeof(struct snd_ctl_tlv) > size) { + dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", + header.length, sizeof(struct snd_ctl_tlv), size); + return -EINVAL; + } + /* be->max is coming from topology */ if (header.length > be->max) { dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", @@ -337,7 +350,8 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - if (cdata->data->size + sizeof(const struct sof_abi_hdr) > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); return -EINVAL; } @@ -353,42 +367,60 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return 0; } -int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, - unsigned int __user *binary_data, - unsigned int size) +int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, + unsigned int size) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct snd_ctl_tlv header; - struct snd_ctl_tlv __user *tlvd = - (struct snd_ctl_tlv __user *)binary_data; - int data_size; - int ret = 0; + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + size_t data_size; + int ret; + int err; /* * Decrement the limit by ext bytes header size to * ensure the user space buffer is not exceeded. */ - size -= sizeof(const struct snd_ctl_tlv); + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + size -= sizeof(struct snd_ctl_tlv); + + ret = pm_runtime_get_sync(scomp->dev); + if (ret < 0 && ret != -EACCES) { + dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); + pm_runtime_put_noidle(scomp->dev); + return ret; + } /* set the ABI header values */ cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; - - /* Prevent read of other kernel data or possibly corrupt response */ - data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + /* get all the component data from DSP */ + ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, + scontrol->cmd, false); + if (ret < 0) + goto out; /* check data size doesn't exceed max coming from topology */ - if (data_size > be->max) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %d.\n", - data_size, be->max); + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", + cdata->data->size, + be->max - sizeof(const struct sof_abi_hdr)); ret = -EINVAL; goto out; } + data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) { + ret = -ENOSPC; + goto out; + } + header.numid = scontrol->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) { @@ -398,7 +430,62 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, if (copy_to_user(tlvd->tlv, cdata->data, data_size)) ret = -EFAULT; - out: + pm_runtime_mark_last_busy(scomp->dev); + err = pm_runtime_put_autosuspend(scomp->dev); + if (err < 0) + dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); + return ret; } + +int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, + unsigned int __user *binary_data, + unsigned int size) +{ + struct soc_bytes_ext *be = + (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct snd_ctl_tlv header; + struct snd_ctl_tlv __user *tlvd = + (struct snd_ctl_tlv __user *)binary_data; + size_t data_size; + + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + size -= sizeof(struct snd_ctl_tlv); + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", + cdata->data->size, + be->max - sizeof(const struct sof_abi_hdr)); + return -EINVAL; + } + + data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = scontrol->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + return -EFAULT; + + return 0; +} |