Commit 0e4ea878 authored by Guennadi Liakhovetski's avatar Guennadi Liakhovetski Committed by Mark Brown

ASoC: SOF: fix range checks

On multiple locations checks are performed of untrusted values after adding
a constant to them. This is wrong, because the addition might overflow and
the result can then pass the check, although the original value is invalid.
Fix multiple such issues by checking the actual value and not a sum of it
and a constant.
Signed-off-by: default avatarGuennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20200917105633.2579047-8-kai.vehmanen@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent db69bcf9
...@@ -229,14 +229,16 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, ...@@ -229,14 +229,16 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
return -EINVAL; return -EINVAL;
} }
size = data->size + sizeof(*data); /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */
if (size > be->max) { if (data->size > be->max - sizeof(*data)) {
dev_err_ratelimited(scomp->dev, dev_err_ratelimited(scomp->dev,
"error: DSP sent %zu bytes max is %d\n", "error: %u bytes of control data is invalid, max is %zu\n",
size, be->max); data->size, be->max - sizeof(*data));
return -EINVAL; return -EINVAL;
} }
size = data->size + sizeof(*data);
/* copy back to kcontrol */ /* copy back to kcontrol */
memcpy(ucontrol->value.bytes.data, data, size); memcpy(ucontrol->value.bytes.data, data, size);
...@@ -252,7 +254,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, ...@@ -252,7 +254,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *scomp = scontrol->scomp; struct snd_soc_component *scomp = scontrol->scomp;
struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_ipc_ctrl_data *cdata = scontrol->control_data;
struct sof_abi_hdr *data = cdata->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)) { if (be->max > sizeof(ucontrol->value.bytes.data)) {
dev_err_ratelimited(scomp->dev, dev_err_ratelimited(scomp->dev,
...@@ -261,13 +263,16 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, ...@@ -261,13 +263,16 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
return -EINVAL; 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, dev_err_ratelimited(scomp->dev,
"error: size too big %zu bytes max is %d\n", "error: data size too big %u bytes max is %zu\n",
size, be->max); data->size, be->max - sizeof(*data));
return -EINVAL; return -EINVAL;
} }
size = data->size + sizeof(*data);
/* copy from kcontrol */ /* copy from kcontrol */
memcpy(data, ucontrol->value.bytes.data, size); memcpy(data, ucontrol->value.bytes.data, size);
...@@ -334,7 +339,8 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, ...@@ -334,7 +339,8 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
return -EINVAL; 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"); dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n");
return -EINVAL; return -EINVAL;
} }
...@@ -420,7 +426,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, ...@@ -420,7 +426,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_tlv header; struct snd_ctl_tlv header;
struct snd_ctl_tlv __user *tlvd = struct snd_ctl_tlv __user *tlvd =
(struct snd_ctl_tlv __user *)binary_data; (struct snd_ctl_tlv __user *)binary_data;
int data_size; size_t data_size;
/* /*
* Decrement the limit by ext bytes header size to * Decrement the limit by ext bytes header size to
...@@ -432,16 +438,16 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, ...@@ -432,16 +438,16 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
cdata->data->magic = SOF_ABI_MAGIC; cdata->data->magic = SOF_ABI_MAGIC;
cdata->data->abi = SOF_ABI_VERSION; 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);
/* check data size doesn't exceed max coming from topology */ /* check data size doesn't exceed max coming from topology */
if (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 %d.\n", dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n",
data_size, be->max); cdata->data->size,
be->max - sizeof(const struct sof_abi_hdr));
return -EINVAL; return -EINVAL;
} }
data_size = cdata->data->size + sizeof(const struct sof_abi_hdr);
header.numid = scontrol->cmd; header.numid = scontrol->cmd;
header.length = data_size; header.length = data_size;
if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv)))
......
...@@ -1150,20 +1150,26 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, ...@@ -1150,20 +1150,26 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp,
struct snd_soc_tplg_bytes_control *control = struct snd_soc_tplg_bytes_control *control =
container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); container_of(hdr, struct snd_soc_tplg_bytes_control, hdr);
struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value;
int max_size = sbe->max; size_t max_size = sbe->max;
size_t priv_size = le32_to_cpu(control->priv.size);
int ret; int ret;
/* init the get/put bytes data */ if (max_size < sizeof(struct sof_ipc_ctrl_data) ||
scontrol->size = sizeof(struct sof_ipc_ctrl_data) + max_size < sizeof(struct sof_abi_hdr)) {
le32_to_cpu(control->priv.size); ret = -EINVAL;
goto out;
}
if (scontrol->size > max_size) { /* init the get/put bytes data */
dev_err(scomp->dev, "err: bytes data size %d exceeds max %d.\n", if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) {
scontrol->size, max_size); dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n",
priv_size, max_size - sizeof(struct sof_ipc_ctrl_data));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size;
scontrol->control_data = kzalloc(max_size, GFP_KERNEL); scontrol->control_data = kzalloc(max_size, GFP_KERNEL);
cdata = scontrol->control_data; cdata = scontrol->control_data;
if (!scontrol->control_data) { if (!scontrol->control_data) {
......
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