Skip to content

Commit f19e102

Browse files
committed
Merge tag 'asoc-fix-v6.10-rc7' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Fixes for v6.10 A few fairly small fixes for ASoC, there's a relatively large set of hardening changes for the cs_dsp firmware file parsing and a couple of other small device specific fixes.
2 parents b469530 + 680e126 commit f19e102

File tree

4 files changed

+176
-71
lines changed

4 files changed

+176
-71
lines changed

drivers/firmware/cirrus/cs_dsp.c

Lines changed: 162 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,9 +1107,16 @@ struct cs_dsp_coeff_parsed_coeff {
11071107
int len;
11081108
};
11091109

1110-
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
1110+
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail,
1111+
const u8 **str)
11111112
{
1112-
int length;
1113+
int length, total_field_len;
1114+
1115+
/* String fields are at least one __le32 */
1116+
if (sizeof(__le32) > avail) {
1117+
*pos = NULL;
1118+
return 0;
1119+
}
11131120

11141121
switch (bytes) {
11151122
case 1:
@@ -1122,10 +1129,16 @@ static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
11221129
return 0;
11231130
}
11241131

1132+
total_field_len = ((length + bytes) + 3) & ~0x03;
1133+
if ((unsigned int)total_field_len > avail) {
1134+
*pos = NULL;
1135+
return 0;
1136+
}
1137+
11251138
if (str)
11261139
*str = *pos + bytes;
11271140

1128-
*pos += ((length + bytes) + 3) & ~0x03;
1141+
*pos += total_field_len;
11291142

11301143
return length;
11311144
}
@@ -1150,71 +1163,134 @@ static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos)
11501163
return val;
11511164
}
11521165

1153-
static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data,
1154-
struct cs_dsp_coeff_parsed_alg *blk)
1166+
static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp,
1167+
const struct wmfw_region *region,
1168+
struct cs_dsp_coeff_parsed_alg *blk)
11551169
{
11561170
const struct wmfw_adsp_alg_data *raw;
1171+
unsigned int data_len = le32_to_cpu(region->len);
1172+
unsigned int pos;
1173+
const u8 *tmp;
1174+
1175+
raw = (const struct wmfw_adsp_alg_data *)region->data;
11571176

11581177
switch (dsp->fw_ver) {
11591178
case 0:
11601179
case 1:
1161-
raw = (const struct wmfw_adsp_alg_data *)*data;
1162-
*data = raw->data;
1180+
if (sizeof(*raw) > data_len)
1181+
return -EOVERFLOW;
11631182

11641183
blk->id = le32_to_cpu(raw->id);
11651184
blk->name = raw->name;
1166-
blk->name_len = strlen(raw->name);
1185+
blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
11671186
blk->ncoeff = le32_to_cpu(raw->ncoeff);
1187+
1188+
pos = sizeof(*raw);
11681189
break;
11691190
default:
1170-
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data);
1171-
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data,
1191+
if (sizeof(raw->id) > data_len)
1192+
return -EOVERFLOW;
1193+
1194+
tmp = region->data;
1195+
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp);
1196+
pos = tmp - region->data;
1197+
1198+
tmp = &region->data[pos];
1199+
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
11721200
&blk->name);
1173-
cs_dsp_coeff_parse_string(sizeof(u16), data, NULL);
1174-
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data);
1201+
if (!tmp)
1202+
return -EOVERFLOW;
1203+
1204+
pos = tmp - region->data;
1205+
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
1206+
if (!tmp)
1207+
return -EOVERFLOW;
1208+
1209+
pos = tmp - region->data;
1210+
if (sizeof(raw->ncoeff) > (data_len - pos))
1211+
return -EOVERFLOW;
1212+
1213+
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp);
1214+
pos += sizeof(raw->ncoeff);
11751215
break;
11761216
}
11771217

1218+
if ((int)blk->ncoeff < 0)
1219+
return -EOVERFLOW;
1220+
11781221
cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
11791222
cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
11801223
cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
1224+
1225+
return pos;
11811226
}
11821227

1183-
static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
1184-
struct cs_dsp_coeff_parsed_coeff *blk)
1228+
static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp,
1229+
const struct wmfw_region *region,
1230+
unsigned int pos,
1231+
struct cs_dsp_coeff_parsed_coeff *blk)
11851232
{
11861233
const struct wmfw_adsp_coeff_data *raw;
1234+
unsigned int data_len = le32_to_cpu(region->len);
1235+
unsigned int blk_len, blk_end_pos;
11871236
const u8 *tmp;
1188-
int length;
1237+
1238+
raw = (const struct wmfw_adsp_coeff_data *)&region->data[pos];
1239+
if (sizeof(raw->hdr) > (data_len - pos))
1240+
return -EOVERFLOW;
1241+
1242+
blk_len = le32_to_cpu(raw->hdr.size);
1243+
if (blk_len > S32_MAX)
1244+
return -EOVERFLOW;
1245+
1246+
if (blk_len > (data_len - pos - sizeof(raw->hdr)))
1247+
return -EOVERFLOW;
1248+
1249+
blk_end_pos = pos + sizeof(raw->hdr) + blk_len;
1250+
1251+
blk->offset = le16_to_cpu(raw->hdr.offset);
1252+
blk->mem_type = le16_to_cpu(raw->hdr.type);
11891253

11901254
switch (dsp->fw_ver) {
11911255
case 0:
11921256
case 1:
1193-
raw = (const struct wmfw_adsp_coeff_data *)*data;
1194-
*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
1257+
if (sizeof(*raw) > (data_len - pos))
1258+
return -EOVERFLOW;
11951259

1196-
blk->offset = le16_to_cpu(raw->hdr.offset);
1197-
blk->mem_type = le16_to_cpu(raw->hdr.type);
11981260
blk->name = raw->name;
1199-
blk->name_len = strlen(raw->name);
1261+
blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
12001262
blk->ctl_type = le16_to_cpu(raw->ctl_type);
12011263
blk->flags = le16_to_cpu(raw->flags);
12021264
blk->len = le32_to_cpu(raw->len);
12031265
break;
12041266
default:
1205-
tmp = *data;
1206-
blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1207-
blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1208-
length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1209-
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp,
1267+
pos += sizeof(raw->hdr);
1268+
tmp = &region->data[pos];
1269+
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
12101270
&blk->name);
1211-
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL);
1212-
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL);
1271+
if (!tmp)
1272+
return -EOVERFLOW;
1273+
1274+
pos = tmp - region->data;
1275+
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL);
1276+
if (!tmp)
1277+
return -EOVERFLOW;
1278+
1279+
pos = tmp - region->data;
1280+
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
1281+
if (!tmp)
1282+
return -EOVERFLOW;
1283+
1284+
pos = tmp - region->data;
1285+
if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) >
1286+
(data_len - pos))
1287+
return -EOVERFLOW;
1288+
12131289
blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1290+
pos += sizeof(raw->ctl_type);
12141291
blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);
1292+
pos += sizeof(raw->flags);
12151293
blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);
1216-
1217-
*data = *data + sizeof(raw->hdr) + length;
12181294
break;
12191295
}
12201296

@@ -1224,6 +1300,8 @@ static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
12241300
cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
12251301
cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
12261302
cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1303+
1304+
return blk_end_pos;
12271305
}
12281306

12291307
static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp,
@@ -1247,12 +1325,16 @@ static int cs_dsp_parse_coeff(struct cs_dsp *dsp,
12471325
struct cs_dsp_alg_region alg_region = {};
12481326
struct cs_dsp_coeff_parsed_alg alg_blk;
12491327
struct cs_dsp_coeff_parsed_coeff coeff_blk;
1250-
const u8 *data = region->data;
1251-
int i, ret;
1328+
int i, pos, ret;
1329+
1330+
pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk);
1331+
if (pos < 0)
1332+
return pos;
12521333

1253-
cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk);
12541334
for (i = 0; i < alg_blk.ncoeff; i++) {
1255-
cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk);
1335+
pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk);
1336+
if (pos < 0)
1337+
return pos;
12561338

12571339
switch (coeff_blk.ctl_type) {
12581340
case WMFW_CTL_TYPE_BYTES:
@@ -1321,6 +1403,10 @@ static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp,
13211403
const struct wmfw_adsp1_sizes *adsp1_sizes;
13221404

13231405
adsp1_sizes = (void *)&firmware->data[pos];
1406+
if (sizeof(*adsp1_sizes) > firmware->size - pos) {
1407+
cs_dsp_err(dsp, "%s: file truncated\n", file);
1408+
return 0;
1409+
}
13241410

13251411
cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,
13261412
le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm),
@@ -1337,6 +1423,10 @@ static unsigned int cs_dsp_adsp2_parse_sizes(struct cs_dsp *dsp,
13371423
const struct wmfw_adsp2_sizes *adsp2_sizes;
13381424

13391425
adsp2_sizes = (void *)&firmware->data[pos];
1426+
if (sizeof(*adsp2_sizes) > firmware->size - pos) {
1427+
cs_dsp_err(dsp, "%s: file truncated\n", file);
1428+
return 0;
1429+
}
13401430

13411431
cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,
13421432
le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym),
@@ -1376,7 +1466,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
13761466
struct regmap *regmap = dsp->regmap;
13771467
unsigned int pos = 0;
13781468
const struct wmfw_header *header;
1379-
const struct wmfw_adsp1_sizes *adsp1_sizes;
13801469
const struct wmfw_footer *footer;
13811470
const struct wmfw_region *region;
13821471
const struct cs_dsp_region *mem;
@@ -1392,10 +1481,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
13921481

13931482
ret = -EINVAL;
13941483

1395-
pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
1396-
if (pos >= firmware->size) {
1397-
cs_dsp_err(dsp, "%s: file too short, %zu bytes\n",
1398-
file, firmware->size);
1484+
if (sizeof(*header) >= firmware->size) {
1485+
ret = -EOVERFLOW;
13991486
goto out_fw;
14001487
}
14011488

@@ -1423,22 +1510,36 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
14231510

14241511
pos = sizeof(*header);
14251512
pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
1513+
if ((pos == 0) || (sizeof(*footer) > firmware->size - pos)) {
1514+
ret = -EOVERFLOW;
1515+
goto out_fw;
1516+
}
14261517

14271518
footer = (void *)&firmware->data[pos];
14281519
pos += sizeof(*footer);
14291520

14301521
if (le32_to_cpu(header->len) != pos) {
1431-
cs_dsp_err(dsp, "%s: unexpected header length %d\n",
1432-
file, le32_to_cpu(header->len));
1522+
ret = -EOVERFLOW;
14331523
goto out_fw;
14341524
}
14351525

14361526
cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file,
14371527
le64_to_cpu(footer->timestamp));
14381528

1439-
while (pos < firmware->size &&
1440-
sizeof(*region) < firmware->size - pos) {
1529+
while (pos < firmware->size) {
1530+
/* Is there enough data for a complete block header? */
1531+
if (sizeof(*region) > firmware->size - pos) {
1532+
ret = -EOVERFLOW;
1533+
goto out_fw;
1534+
}
1535+
14411536
region = (void *)&(firmware->data[pos]);
1537+
1538+
if (le32_to_cpu(region->len) > firmware->size - pos - sizeof(*region)) {
1539+
ret = -EOVERFLOW;
1540+
goto out_fw;
1541+
}
1542+
14421543
region_name = "Unknown";
14431544
reg = 0;
14441545
text = NULL;
@@ -1495,16 +1596,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
14951596
regions, le32_to_cpu(region->len), offset,
14961597
region_name);
14971598

1498-
if (le32_to_cpu(region->len) >
1499-
firmware->size - pos - sizeof(*region)) {
1500-
cs_dsp_err(dsp,
1501-
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
1502-
file, regions, region_name,
1503-
le32_to_cpu(region->len), firmware->size);
1504-
ret = -EINVAL;
1505-
goto out_fw;
1506-
}
1507-
15081599
if (text) {
15091600
memcpy(text, region->data, le32_to_cpu(region->len));
15101601
cs_dsp_info(dsp, "%s: %s\n", file, text);
@@ -1555,6 +1646,9 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
15551646
cs_dsp_buf_free(&buf_list);
15561647
kfree(text);
15571648

1649+
if (ret == -EOVERFLOW)
1650+
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
1651+
15581652
return ret;
15591653
}
15601654

@@ -2122,10 +2216,20 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
21222216
pos = le32_to_cpu(hdr->len);
21232217

21242218
blocks = 0;
2125-
while (pos < firmware->size &&
2126-
sizeof(*blk) < firmware->size - pos) {
2219+
while (pos < firmware->size) {
2220+
/* Is there enough data for a complete block header? */
2221+
if (sizeof(*blk) > firmware->size - pos) {
2222+
ret = -EOVERFLOW;
2223+
goto out_fw;
2224+
}
2225+
21272226
blk = (void *)(&firmware->data[pos]);
21282227

2228+
if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) {
2229+
ret = -EOVERFLOW;
2230+
goto out_fw;
2231+
}
2232+
21292233
type = le16_to_cpu(blk->type);
21302234
offset = le16_to_cpu(blk->offset);
21312235
version = le32_to_cpu(blk->ver) >> 8;
@@ -2222,17 +2326,6 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
22222326
}
22232327

22242328
if (reg) {
2225-
if (le32_to_cpu(blk->len) >
2226-
firmware->size - pos - sizeof(*blk)) {
2227-
cs_dsp_err(dsp,
2228-
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
2229-
file, blocks, region_name,
2230-
le32_to_cpu(blk->len),
2231-
firmware->size);
2232-
ret = -EINVAL;
2233-
goto out_fw;
2234-
}
2235-
22362329
buf = cs_dsp_buf_alloc(blk->data,
22372330
le32_to_cpu(blk->len),
22382331
&buf_list);
@@ -2272,6 +2365,10 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
22722365
regmap_async_complete(regmap);
22732366
cs_dsp_buf_free(&buf_list);
22742367
kfree(text);
2368+
2369+
if (ret == -EOVERFLOW)
2370+
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
2371+
22752372
return ret;
22762373
}
22772374

0 commit comments

Comments
 (0)