| @@ -1107,9 +1107,16 @@ struct cs_dsp_coeff_parsed_coeff { · 1106 unmodified lines |
| 1107 | 1107 | int len; |
| 1108 | 1108 | }; |
| 1109 | 1109 | |
| 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) |
| 1111 | 1112 | { |
| 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 | + } |
| 1113 | 1120 | |
| 1114 | 1121 | switch (bytes) { |
| 1115 | 1122 | case 1: |
| @@ -1122,10 +1129,16 @@ static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) · 6 unmodified lines |
| 1122 | 1129 | return 0; |
| 1123 | 1130 | } |
| 1124 | 1131 | |
| 1132 | + total_field_len = ((length + bytes) + 3) & ~0x03; |
| 1133 | + if ((unsigned int)total_field_len > avail) { |
| 1134 | + *pos = NULL; |
| 1135 | + return 0; |
| 1136 | + } |
| 1137 | + |
| 1125 | 1138 | if (str) |
| 1126 | 1139 | *str = *pos + bytes; |
| 1127 | 1140 | |
| 1128 | | − *pos += ((length + bytes) + 3) & ~0x03; |
| 1141 | + *pos += total_field_len; |
| 1129 | 1142 | |
| 1130 | 1143 | return length; |
| 1131 | 1144 | } |
| @@ -1150,71 +1163,134 @@ static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos) · 18 unmodified lines |
| 1150 | 1163 | return val; |
| 1151 | 1164 | } |
| 1152 | 1165 | |
| 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) |
| 1155 | 1169 | { |
| 1156 | 1170 | 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; |
| 1157 | 1176 | |
| 1158 | 1177 | switch (dsp->fw_ver) { |
| 1159 | 1178 | case 0: |
| 1160 | 1179 | case 1: |
| 1161 | | − raw = (const struct wmfw_adsp_alg_data *)*data; |
| 1162 | | − *data = raw->data; |
| 1180 | + if (sizeof(*raw) > data_len) |
| 1181 | + return -EOVERFLOW; |
| 1163 | 1182 | |
| 1164 | 1183 | blk->id = le32_to_cpu(raw->id); |
| 1165 | 1184 | blk->name = raw->name; |
| 1166 | | − blk->name_len = strlen(raw->name); |
| 1185 | + blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name)); |
| 1167 | 1186 | blk->ncoeff = le32_to_cpu(raw->ncoeff); |
| 1187 | + |
| 1188 | + pos = sizeof(*raw); |
| 1168 | 1189 | break; |
| 1169 | 1190 | 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 = ®ion->data[pos]; |
| 1199 | + blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, |
| 1172 | 1200 | &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); |
| 1175 | 1215 | break; |
| 1176 | 1216 | } |
| 1177 | 1217 | |
| 1218 | + if ((int)blk->ncoeff < 0) |
| 1219 | + return -EOVERFLOW; |
| 1220 | + |
| 1178 | 1221 | cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); |
| 1179 | 1222 | cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); |
| 1180 | 1223 | cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); |
| 1224 | + |
| 1225 | + return pos; |
| 1181 | 1226 | } |
| 1182 | 1227 | |
| 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) |
| 1185 | 1232 | { |
| 1186 | 1233 | 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; |
| 1187 | 1236 | const u8 *tmp; |
| 1188 | | − int length; |
| 1237 | + |
| 1238 | + raw = (const struct wmfw_adsp_coeff_data *)®ion->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); |
| 1189 | 1253 | |
| 1190 | 1254 | switch (dsp->fw_ver) { |
| 1191 | 1255 | case 0: |
| 1192 | 1256 | 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; |
| 1195 | 1259 | |
| 1196 | | − blk->offset = le16_to_cpu(raw->hdr.offset); |
| 1197 | | − blk->mem_type = le16_to_cpu(raw->hdr.type); |
| 1198 | 1260 | blk->name = raw->name; |
| 1199 | | − blk->name_len = strlen(raw->name); |
| 1261 | + blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name)); |
| 1200 | 1262 | blk->ctl_type = le16_to_cpu(raw->ctl_type); |
| 1201 | 1263 | blk->flags = le16_to_cpu(raw->flags); |
| 1202 | 1264 | blk->len = le32_to_cpu(raw->len); |
| 1203 | 1265 | break; |
| 1204 | 1266 | 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 = ®ion->data[pos]; |
| 1269 | + blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, |
| 1210 | 1270 | &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 | + |
| 1213 | 1289 | blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp); |
| 1290 | + pos += sizeof(raw->ctl_type); |
| 1214 | 1291 | blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp); |
| 1292 | + pos += sizeof(raw->flags); |
| 1215 | 1293 | blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp); |
| 1216 | | − |
| 1217 | | − *data = *data + sizeof(raw->hdr) + length; |
| 1218 | 1294 | break; |
| 1219 | 1295 | } |
| 1220 | 1296 | |
| @@ -1224,6 +1300,8 @@ static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data, · 3 unmodified lines |
| 1224 | 1300 | cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); |
| 1225 | 1301 | cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); |
| 1226 | 1302 | cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); |
| 1303 | + |
| 1304 | + return blk_end_pos; |
| 1227 | 1305 | } |
| 1228 | 1306 | |
| 1229 | 1307 | 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, · 17 unmodified lines |
| 1247 | 1325 | struct cs_dsp_alg_region alg_region = {}; |
| 1248 | 1326 | struct cs_dsp_coeff_parsed_alg alg_blk; |
| 1249 | 1327 | 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; |
| 1252 | 1333 | |
| 1253 | | − cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk); |
| 1254 | 1334 | 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; |
| 1256 | 1338 | |
| 1257 | 1339 | switch (coeff_blk.ctl_type) { |
| 1258 | 1340 | case WMFW_CTL_TYPE_BYTES: |
| @@ -1321,6 +1403,10 @@ static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp, · 62 unmodified lines |
| 1321 | 1403 | const struct wmfw_adsp1_sizes *adsp1_sizes; |
| 1322 | 1404 | |
| 1323 | 1405 | 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 | + } |
| 1324 | 1410 | |
| 1325 | 1411 | cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, |
| 1326 | 1412 | 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, · 10 unmodified lines |
| 1337 | 1423 | const struct wmfw_adsp2_sizes *adsp2_sizes; |
| 1338 | 1424 | |
| 1339 | 1425 | 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 | + } |
| 1340 | 1430 | |
| 1341 | 1431 | cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, |
| 1342 | 1432 | 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, · 33 unmodified lines |
| 1376 | 1466 | struct regmap *regmap = dsp->regmap; |
| 1377 | 1467 | unsigned int pos = 0; |
| 1378 | 1468 | const struct wmfw_header *header; |
| 1379 | | − const struct wmfw_adsp1_sizes *adsp1_sizes; |
| 1380 | 1469 | const struct wmfw_footer *footer; |
| 1381 | 1470 | const struct wmfw_region *region; |
| 1382 | 1471 | const struct cs_dsp_region *mem; |
| @@ -1392,10 +1481,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, · 9 unmodified lines |
| 1392 | 1481 | |
| 1393 | 1482 | ret = -EINVAL; |
| 1394 | 1483 | |
| 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; |
| 1399 | 1486 | goto out_fw; |
| 1400 | 1487 | } |
| 1401 | 1488 | |
| @@ -1423,22 +1510,36 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, · 21 unmodified lines |
| 1423 | 1510 | |
| 1424 | 1511 | pos = sizeof(*header); |
| 1425 | 1512 | 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 | + } |
| 1426 | 1517 | |
| 1427 | 1518 | footer = (void *)&firmware->data[pos]; |
| 1428 | 1519 | pos += sizeof(*footer); |
| 1429 | 1520 | |
| 1430 | 1521 | 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; |
| 1433 | 1523 | goto out_fw; |
| 1434 | 1524 | } |
| 1435 | 1525 | |
| 1436 | 1526 | cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, |
| 1437 | 1527 | le64_to_cpu(footer->timestamp)); |
| 1438 | 1528 | |
| 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 | + |
| 1441 | 1536 | 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 | + |
| 1442 | 1543 | region_name = "Unknown"; |
| 1443 | 1544 | reg = 0; |
| 1444 | 1545 | text = NULL; |
| @@ -1495,16 +1596,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware, · 50 unmodified lines |
| 1495 | 1596 | regions, le32_to_cpu(region->len), offset, |
| 1496 | 1597 | region_name); |
| 1497 | 1598 | |
| 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 | | − |
| 1508 | 1599 | if (text) { |
| 1509 | 1600 | memcpy(text, region->data, le32_to_cpu(region->len)); |
| 1510 | 1601 | cs_dsp_info(dsp, "%s: %s\n", file, text); |
| @@ -1555,6 +1646,9 @@ out_fw: · 44 unmodified lines |
| 1555 | 1646 | cs_dsp_buf_free(&buf_list); |
| 1556 | 1647 | kfree(text); |
| 1557 | 1648 | |
| 1649 | + if (ret == -EOVERFLOW) |
| 1650 | + cs_dsp_err(dsp, "%s: file content overflows file data\n", file); |
| 1651 | + |
| 1558 | 1652 | return ret; |
| 1559 | 1653 | } |
| 1560 | 1654 | |
| @@ -2122,10 +2216,20 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware · 561 unmodified lines |
| 2122 | 2216 | pos = le32_to_cpu(hdr->len); |
| 2123 | 2217 | |
| 2124 | 2218 | 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 | + |
| 2127 | 2226 | blk = (void *)(&firmware->data[pos]); |
| 2128 | 2227 | |
| 2228 | + if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) { |
| 2229 | + ret = -EOVERFLOW; |
| 2230 | + goto out_fw; |
| 2231 | + } |
| 2232 | + |
| 2129 | 2233 | type = le16_to_cpu(blk->type); |
| 2130 | 2234 | offset = le16_to_cpu(blk->offset); |
| 2131 | 2235 | 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 · 90 unmodified lines |
| 2222 | 2326 | } |
| 2223 | 2327 | |
| 2224 | 2328 | 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 | | − |
| 2236 | 2329 | buf = cs_dsp_buf_alloc(blk->data, |
| 2237 | 2330 | le32_to_cpu(blk->len), |
| 2238 | 2331 | &buf_list); |
| @@ -2272,6 +2365,10 @@ out_fw: · 33 unmodified lines |
| 2272 | 2365 | regmap_async_complete(regmap); |
| 2273 | 2366 | cs_dsp_buf_free(&buf_list); |
| 2274 | 2367 | kfree(text); |
| 2368 | + |
| 2369 | + if (ret == -EOVERFLOW) |
| 2370 | + cs_dsp_err(dsp, "%s: file content overflows file data\n", file); |
| 2371 | + |
| 2275 | 2372 | return ret; |
| 2276 | 2373 | } |
| 2277 | 2374 | |