ASoC: tas2781: bound firmware description string parsing

The TAS2781 firmware parser reads several variable-length description
strings with strlen() before checking that the string terminator is
present inside the firmware blob. A malformed firmware image without a
NUL terminator can therefore make the parser walk past the end of the
firmware buffer before the later size checks run.

Add a small bounded string-length helper and use it for all description
fields that are parsed from the firmware buffer. Keep the existing size
checks for the fixed bytes that follow each string.

Fixes: 915f5eadeb ("ASoC: tas2781: firmware lib")
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Link: https://patch.msgid.link/20260706144540.93929-1-pengpeng@iscas.ac.cn
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Pengpeng Hou 2026-07-06 22:45:39 +08:00 committed by Mark Brown
parent 27d090f3cc
commit bc889dfcea
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/limits.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@ -1100,13 +1101,42 @@ static int tasdevice_load_block_kernel(
return 0;
}
static int tasdevice_fw_strnlen(const struct firmware *fmw, int offset)
{
const u8 *start;
const u8 *nul;
size_t remaining;
size_t len;
if (offset < 0 || offset >= fmw->size)
return -EINVAL;
start = fmw->data + offset;
remaining = fmw->size - offset;
nul = memchr(start, '\0', remaining);
if (!nul)
return -EINVAL;
len = nul - start;
if (len > INT_MAX)
return -EOVERFLOW;
return len;
}
static int fw_parse_variable_hdr(struct tasdevice_priv
*tas_priv, struct tasdevice_dspfw_hdr *fw_hdr,
const struct firmware *fmw, int offset)
{
const unsigned char *buf = fmw->data;
int len = strlen((char *)&buf[offset]);
int len;
len = tasdevice_fw_strnlen(fmw, offset);
if (len < 0) {
dev_err(tas_priv->dev, "%s: Description error\n", __func__);
offset = len;
goto out;
}
len++;
if (offset + len + 8 > fmw->size) {
@ -1238,7 +1268,12 @@ static int fw_parse_data(struct tasdevice_fw *tas_fmw,
memcpy(img_data->name, &data[offset], 64);
offset += 64;
n = strlen((char *)&data[offset]);
n = tasdevice_fw_strnlen(fmw, offset);
if (n < 0) {
dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
offset = n;
goto out;
}
n++;
if (offset + n + 2 > fmw->size) {
dev_err(tas_fmw->dev, "%s: Description error\n", __func__);
@ -1309,7 +1344,12 @@ static int fw_parse_program_data(struct tasdevice_priv *tas_priv,
}
offset += 64;
n = strlen((char *)&buf[offset]);
n = tasdevice_fw_strnlen(fmw, offset);
if (n < 0) {
dev_err(tas_priv->dev, "Description err\n");
offset = n;
goto out;
}
/* skip '\0' and 5 unused bytes */
n += 6;
if (offset + n > fmw->size) {
@ -1372,7 +1412,12 @@ static int fw_parse_configuration_data(
memcpy(config->name, &data[offset], 64);
offset += 64;
n = strlen((char *)&data[offset]);
n = tasdevice_fw_strnlen(fmw, offset);
if (n < 0) {
dev_err(tas_priv->dev, "Description err\n");
offset = n;
goto out;
}
n += 15;
if (offset + n > fmw->size) {
dev_err(tas_priv->dev, "Description err\n");
@ -2134,7 +2179,8 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
{
struct tasdevice_calibration *calibration;
unsigned char *data = (unsigned char *)fmw->data;
unsigned int i, n;
unsigned int i;
int n;
if (offset + 2 > fmw->size) {
dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__);
@ -2166,7 +2212,12 @@ static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv,
calibration = &(tas_fmw->calibrations[i]);
offset += 64;
n = strlen((char *)&data[offset]);
n = tasdevice_fw_strnlen(fmw, offset);
if (n < 0) {
dev_err(tas_priv->dev, "Description err\n");
offset = n;
goto out;
}
/* skip '\0' and 2 unused bytes */
n += 3;
if (offset + n > fmw->size) {