@@ -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
12291307static 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