Skip to content

Commit 9b48104

Browse files
committed
Merge tag 'sound-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound fixes from Takashi Iwai: "The majority of changes here are small device-specific fixes for ASoC SOF / Intel and usual HD-audio quirks. The only significant high LOC is found in the Cirrus firmware driver, but all those are for hardening against malicious firmware blobs, and they look fine for taking as a last minute fix, too" * tag 'sound-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: ALSA: hda/realtek: Enable Mute LED on HP 250 G7 firmware: cs_dsp: Use strnlen() on name fields in V1 wmfw files ALSA: hda/realtek: Limit mic boost on VAIO PRO PX ALSA: hda: cs35l41: Fix swapped l/r audio channels for Lenovo ThinBook 13x Gen4 ASoC: SOF: Intel: hda-pcm: Limit the maximum number of periods by MAX_BDL_ENTRIES ASoC: rt711-sdw: add missing readable registers ASoC: SOF: Intel: hda: fix null deref on system suspend entry ALSA: hda/realtek: add quirk for Clevo V5[46]0TU firmware: cs_dsp: Prevent buffer overrun when processing V2 alg headers firmware: cs_dsp: Validate payload length before processing block firmware: cs_dsp: Return error if block header overflows file firmware: cs_dsp: Fix overflow checking of wmfw header
2 parents 5d4c851 + f19e102 commit 9b48104

File tree

6 files changed

+182
-73
lines changed

6 files changed

+182
-73
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)