Skip to content

Commit fe8bc31

Browse files
committed
firmware: cs_dsp: Prevent buffer overrun when processing V2 alg headers
jira LE-3201 cve CVE-2024-41038 Rebuild_History Non-Buildable kernel-rt-4.18.0-553.22.1.rt7.363.el8_10 commit-author Richard Fitzgerald <rf@opensource.cirrus.com> commit 2163aff 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 <rf@opensource.cirrus.com> Fixes: f6bc909 ("firmware: cs_dsp: add driver to support firmware loading on Cirrus Logic DSPs") Link: https://patch.msgid.link/20240627141432.93056-5-rf@opensource.cirrus.com Signed-off-by: Mark Brown <broonie@kernel.org> (cherry picked from commit 2163aff) Signed-off-by: Jonathan Maple <jmaple@ciq.com>
1 parent 48779a7 commit fe8bc31

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
@@ -1053,9 +1053,16 @@ struct cs_dsp_coeff_parsed_coeff {
10531053
int len;
10541054
};
10551055

1056-
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
1056+
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail,
1057+
const u8 **str)
10571058
{
1058-
int length;
1059+
int length, total_field_len;
1060+
1061+
/* String fields are at least one __le32 */
1062+
if (sizeof(__le32) > avail) {
1063+
*pos = NULL;
1064+
return 0;
1065+
}
10591066

10601067
switch (bytes) {
10611068
case 1:
@@ -1068,10 +1075,16 @@ static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
10681075
return 0;
10691076
}
10701077

1078+
total_field_len = ((length + bytes) + 3) & ~0x03;
1079+
if ((unsigned int)total_field_len > avail) {
1080+
*pos = NULL;
1081+
return 0;
1082+
}
1083+
10711084
if (str)
10721085
*str = *pos + bytes;
10731086

1074-
*pos += ((length + bytes) + 3) & ~0x03;
1087+
*pos += total_field_len;
10751088

10761089
return length;
10771090
}
@@ -1096,71 +1109,134 @@ static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos)
10961109
return val;
10971110
}
10981111

1099-
static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data,
1100-
struct cs_dsp_coeff_parsed_alg *blk)
1112+
static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp,
1113+
const struct wmfw_region *region,
1114+
struct cs_dsp_coeff_parsed_alg *blk)
11011115
{
11021116
const struct wmfw_adsp_alg_data *raw;
1117+
unsigned int data_len = le32_to_cpu(region->len);
1118+
unsigned int pos;
1119+
const u8 *tmp;
1120+
1121+
raw = (const struct wmfw_adsp_alg_data *)region->data;
11031122

11041123
switch (dsp->fw_ver) {
11051124
case 0:
11061125
case 1:
1107-
raw = (const struct wmfw_adsp_alg_data *)*data;
1108-
*data = raw->data;
1126+
if (sizeof(*raw) > data_len)
1127+
return -EOVERFLOW;
11091128

11101129
blk->id = le32_to_cpu(raw->id);
11111130
blk->name = raw->name;
11121131
blk->name_len = strlen(raw->name);
11131132
blk->ncoeff = le32_to_cpu(raw->ncoeff);
1133+
1134+
pos = sizeof(*raw);
11141135
break;
11151136
default:
1116-
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data);
1117-
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data,
1137+
if (sizeof(raw->id) > data_len)
1138+
return -EOVERFLOW;
1139+
1140+
tmp = region->data;
1141+
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp);
1142+
pos = tmp - region->data;
1143+
1144+
tmp = &region->data[pos];
1145+
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
11181146
&blk->name);
1119-
cs_dsp_coeff_parse_string(sizeof(u16), data, NULL);
1120-
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data);
1147+
if (!tmp)
1148+
return -EOVERFLOW;
1149+
1150+
pos = tmp - region->data;
1151+
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
1152+
if (!tmp)
1153+
return -EOVERFLOW;
1154+
1155+
pos = tmp - region->data;
1156+
if (sizeof(raw->ncoeff) > (data_len - pos))
1157+
return -EOVERFLOW;
1158+
1159+
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp);
1160+
pos += sizeof(raw->ncoeff);
11211161
break;
11221162
}
11231163

1164+
if ((int)blk->ncoeff < 0)
1165+
return -EOVERFLOW;
1166+
11241167
cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
11251168
cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
11261169
cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
1170+
1171+
return pos;
11271172
}
11281173

1129-
static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
1130-
struct cs_dsp_coeff_parsed_coeff *blk)
1174+
static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp,
1175+
const struct wmfw_region *region,
1176+
unsigned int pos,
1177+
struct cs_dsp_coeff_parsed_coeff *blk)
11311178
{
11321179
const struct wmfw_adsp_coeff_data *raw;
1180+
unsigned int data_len = le32_to_cpu(region->len);
1181+
unsigned int blk_len, blk_end_pos;
11331182
const u8 *tmp;
1134-
int length;
1183+
1184+
raw = (const struct wmfw_adsp_coeff_data *)&region->data[pos];
1185+
if (sizeof(raw->hdr) > (data_len - pos))
1186+
return -EOVERFLOW;
1187+
1188+
blk_len = le32_to_cpu(raw->hdr.size);
1189+
if (blk_len > S32_MAX)
1190+
return -EOVERFLOW;
1191+
1192+
if (blk_len > (data_len - pos - sizeof(raw->hdr)))
1193+
return -EOVERFLOW;
1194+
1195+
blk_end_pos = pos + sizeof(raw->hdr) + blk_len;
1196+
1197+
blk->offset = le16_to_cpu(raw->hdr.offset);
1198+
blk->mem_type = le16_to_cpu(raw->hdr.type);
11351199

11361200
switch (dsp->fw_ver) {
11371201
case 0:
11381202
case 1:
1139-
raw = (const struct wmfw_adsp_coeff_data *)*data;
1140-
*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size);
1203+
if (sizeof(*raw) > (data_len - pos))
1204+
return -EOVERFLOW;
11411205

1142-
blk->offset = le16_to_cpu(raw->hdr.offset);
1143-
blk->mem_type = le16_to_cpu(raw->hdr.type);
11441206
blk->name = raw->name;
11451207
blk->name_len = strlen(raw->name);
11461208
blk->ctl_type = le16_to_cpu(raw->ctl_type);
11471209
blk->flags = le16_to_cpu(raw->flags);
11481210
blk->len = le32_to_cpu(raw->len);
11491211
break;
11501212
default:
1151-
tmp = *data;
1152-
blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp);
1153-
blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp);
1154-
length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
1155-
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp,
1213+
pos += sizeof(raw->hdr);
1214+
tmp = &region->data[pos];
1215+
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
11561216
&blk->name);
1157-
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL);
1158-
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL);
1217+
if (!tmp)
1218+
return -EOVERFLOW;
1219+
1220+
pos = tmp - region->data;
1221+
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL);
1222+
if (!tmp)
1223+
return -EOVERFLOW;
1224+
1225+
pos = tmp - region->data;
1226+
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
1227+
if (!tmp)
1228+
return -EOVERFLOW;
1229+
1230+
pos = tmp - region->data;
1231+
if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) >
1232+
(data_len - pos))
1233+
return -EOVERFLOW;
1234+
11591235
blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
1236+
pos += sizeof(raw->ctl_type);
11601237
blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);
1238+
pos += sizeof(raw->flags);
11611239
blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);
1162-
1163-
*data = *data + sizeof(raw->hdr) + length;
11641240
break;
11651241
}
11661242

@@ -1170,6 +1246,8 @@ static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
11701246
cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
11711247
cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
11721248
cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
1249+
1250+
return blk_end_pos;
11731251
}
11741252

11751253
static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp,
@@ -1193,12 +1271,16 @@ static int cs_dsp_parse_coeff(struct cs_dsp *dsp,
11931271
struct cs_dsp_alg_region alg_region = {};
11941272
struct cs_dsp_coeff_parsed_alg alg_blk;
11951273
struct cs_dsp_coeff_parsed_coeff coeff_blk;
1196-
const u8 *data = region->data;
1197-
int i, ret;
1274+
int i, pos, ret;
1275+
1276+
pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk);
1277+
if (pos < 0)
1278+
return pos;
11981279

1199-
cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk);
12001280
for (i = 0; i < alg_blk.ncoeff; i++) {
1201-
cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk);
1281+
pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk);
1282+
if (pos < 0)
1283+
return pos;
12021284

12031285
switch (coeff_blk.ctl_type) {
12041286
case WMFW_CTL_TYPE_BYTES:

0 commit comments

Comments
 (0)