Skip to content

Commit 2163aff

Browse files
rfvirgilbroonie
authored andcommitted
firmware: cs_dsp: Prevent buffer overrun when processing V2 alg headers
Check that all fields of a V2 algorithm header fit into the available firmware data buffer. The wmfw V2 format introduced variable-length strings in the algorithm block header. This means the overall header length is variable, and the position of most fields varies depending on the length of the string fields. Each field must be checked to ensure that it does not overflow the firmware data buffer. As this ia bugfix patch, the fixes avoid making any significant change to the existing code. This makes it easier to review and less likely to introduce new bugs. Signed-off-by: Richard Fitzgerald <[email protected]> Fixes: f6bc909 ("firmware: cs_dsp: add driver to support firmware loading on Cirrus Logic DSPs") Link: https://patch.msgid.link/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 6598afa commit 2163aff

File tree

1 file changed

+113
-31
lines changed

1 file changed

+113
-31
lines changed

drivers/firmware/cirrus/cs_dsp.c

Lines changed: 113 additions & 31 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;
11661185
blk->name_len = strlen(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;
11991261
blk->name_len = strlen(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:

0 commit comments

Comments
 (0)