1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00

ASoC: Fixes for v6.10

A few fairly small fixes for ASoC, there's a relatively large set of
 hardening changes for the cs_dsp firmware file parsing and a couple of
 other small device specific fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmaP7ksACgkQJNaLcl1U
 h9Cpxgf/Sa+bDU17hG8pcS7WkPcBzZcwnbjEr/6rn2iilscZYn8YwhrXXadSMZp/
 nAawm+ix+ErlfJZaR7kTbRQsthFVnYxyVjpLrWfW3iCdqD/jw00pzxjU7r8/uNKM
 Sp2idj9Y0mH0ggfNQMzbnhXE09uY6gZqAf1KjwGmC59aUu6imVIEX3i0Swfetljs
 1Ndc5Ub75ouRrbC20BVHMLm7hbOY8+k+gs9GUhEvanVgJ9f3pyCPNwJvWCVRFq1q
 5aZYjqMBNuuaxZqtKhnUlddFTe7NLXffulFhrf8T/n2TvMvq3B+2jHoAXHoNTLV4
 c9WZ6PmDsk6gKaG8/uZfOgMFzUz46w==
 =gHJp
 -----END PGP SIGNATURE-----

Merge tag 'asoc-fix-v6.10-rc7' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus

ASoC: Fixes for v6.10

A few fairly small fixes for ASoC, there's a relatively large set of
hardening changes for the cs_dsp firmware file parsing and a couple of
other small device specific fixes.
This commit is contained in:
Takashi Iwai 2024-07-11 17:11:50 +02:00
commit f19e1027f6
4 changed files with 178 additions and 73 deletions

View file

@ -1107,9 +1107,16 @@ struct cs_dsp_coeff_parsed_coeff {
int len; int len;
}; };
static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str) static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, unsigned int avail,
const u8 **str)
{ {
int length; int length, total_field_len;
/* String fields are at least one __le32 */
if (sizeof(__le32) > avail) {
*pos = NULL;
return 0;
}
switch (bytes) { switch (bytes) {
case 1: case 1:
@ -1122,10 +1129,16 @@ static int cs_dsp_coeff_parse_string(int bytes, const u8 **pos, const u8 **str)
return 0; return 0;
} }
total_field_len = ((length + bytes) + 3) & ~0x03;
if ((unsigned int)total_field_len > avail) {
*pos = NULL;
return 0;
}
if (str) if (str)
*str = *pos + bytes; *str = *pos + bytes;
*pos += ((length + bytes) + 3) & ~0x03; *pos += total_field_len;
return length; return length;
} }
@ -1150,71 +1163,134 @@ static int cs_dsp_coeff_parse_int(int bytes, const u8 **pos)
return val; return val;
} }
static inline void cs_dsp_coeff_parse_alg(struct cs_dsp *dsp, const u8 **data, static int cs_dsp_coeff_parse_alg(struct cs_dsp *dsp,
struct cs_dsp_coeff_parsed_alg *blk) const struct wmfw_region *region,
struct cs_dsp_coeff_parsed_alg *blk)
{ {
const struct wmfw_adsp_alg_data *raw; const struct wmfw_adsp_alg_data *raw;
unsigned int data_len = le32_to_cpu(region->len);
unsigned int pos;
const u8 *tmp;
raw = (const struct wmfw_adsp_alg_data *)region->data;
switch (dsp->fw_ver) { switch (dsp->fw_ver) {
case 0: case 0:
case 1: case 1:
raw = (const struct wmfw_adsp_alg_data *)*data; if (sizeof(*raw) > data_len)
*data = raw->data; return -EOVERFLOW;
blk->id = le32_to_cpu(raw->id); blk->id = le32_to_cpu(raw->id);
blk->name = raw->name; blk->name = raw->name;
blk->name_len = strlen(raw->name); blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
blk->ncoeff = le32_to_cpu(raw->ncoeff); blk->ncoeff = le32_to_cpu(raw->ncoeff);
pos = sizeof(*raw);
break; break;
default: default:
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), data); if (sizeof(raw->id) > data_len)
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), data, return -EOVERFLOW;
tmp = region->data;
blk->id = cs_dsp_coeff_parse_int(sizeof(raw->id), &tmp);
pos = tmp - region->data;
tmp = &region->data[pos];
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
&blk->name); &blk->name);
cs_dsp_coeff_parse_string(sizeof(u16), data, NULL); if (!tmp)
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), data); return -EOVERFLOW;
pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
if (sizeof(raw->ncoeff) > (data_len - pos))
return -EOVERFLOW;
blk->ncoeff = cs_dsp_coeff_parse_int(sizeof(raw->ncoeff), &tmp);
pos += sizeof(raw->ncoeff);
break; break;
} }
if ((int)blk->ncoeff < 0)
return -EOVERFLOW;
cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id); cs_dsp_dbg(dsp, "Algorithm ID: %#x\n", blk->id);
cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name); cs_dsp_dbg(dsp, "Algorithm name: %.*s\n", blk->name_len, blk->name);
cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff); cs_dsp_dbg(dsp, "# of coefficient descriptors: %#x\n", blk->ncoeff);
return pos;
} }
static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data, static int cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp,
struct cs_dsp_coeff_parsed_coeff *blk) const struct wmfw_region *region,
unsigned int pos,
struct cs_dsp_coeff_parsed_coeff *blk)
{ {
const struct wmfw_adsp_coeff_data *raw; const struct wmfw_adsp_coeff_data *raw;
unsigned int data_len = le32_to_cpu(region->len);
unsigned int blk_len, blk_end_pos;
const u8 *tmp; const u8 *tmp;
int length;
raw = (const struct wmfw_adsp_coeff_data *)&region->data[pos];
if (sizeof(raw->hdr) > (data_len - pos))
return -EOVERFLOW;
blk_len = le32_to_cpu(raw->hdr.size);
if (blk_len > S32_MAX)
return -EOVERFLOW;
if (blk_len > (data_len - pos - sizeof(raw->hdr)))
return -EOVERFLOW;
blk_end_pos = pos + sizeof(raw->hdr) + blk_len;
blk->offset = le16_to_cpu(raw->hdr.offset);
blk->mem_type = le16_to_cpu(raw->hdr.type);
switch (dsp->fw_ver) { switch (dsp->fw_ver) {
case 0: case 0:
case 1: case 1:
raw = (const struct wmfw_adsp_coeff_data *)*data; if (sizeof(*raw) > (data_len - pos))
*data = *data + sizeof(raw->hdr) + le32_to_cpu(raw->hdr.size); return -EOVERFLOW;
blk->offset = le16_to_cpu(raw->hdr.offset);
blk->mem_type = le16_to_cpu(raw->hdr.type);
blk->name = raw->name; blk->name = raw->name;
blk->name_len = strlen(raw->name); blk->name_len = strnlen(raw->name, ARRAY_SIZE(raw->name));
blk->ctl_type = le16_to_cpu(raw->ctl_type); blk->ctl_type = le16_to_cpu(raw->ctl_type);
blk->flags = le16_to_cpu(raw->flags); blk->flags = le16_to_cpu(raw->flags);
blk->len = le32_to_cpu(raw->len); blk->len = le32_to_cpu(raw->len);
break; break;
default: default:
tmp = *data; pos += sizeof(raw->hdr);
blk->offset = cs_dsp_coeff_parse_int(sizeof(raw->hdr.offset), &tmp); tmp = &region->data[pos];
blk->mem_type = cs_dsp_coeff_parse_int(sizeof(raw->hdr.type), &tmp); blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos,
length = cs_dsp_coeff_parse_int(sizeof(raw->hdr.size), &tmp);
blk->name_len = cs_dsp_coeff_parse_string(sizeof(u8), &tmp,
&blk->name); &blk->name);
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, NULL); if (!tmp)
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, NULL); return -EOVERFLOW;
blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);
blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);
*data = *data + sizeof(raw->hdr) + length; pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u8), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
cs_dsp_coeff_parse_string(sizeof(u16), &tmp, data_len - pos, NULL);
if (!tmp)
return -EOVERFLOW;
pos = tmp - region->data;
if (sizeof(raw->ctl_type) + sizeof(raw->flags) + sizeof(raw->len) >
(data_len - pos))
return -EOVERFLOW;
blk->ctl_type = cs_dsp_coeff_parse_int(sizeof(raw->ctl_type), &tmp);
pos += sizeof(raw->ctl_type);
blk->flags = cs_dsp_coeff_parse_int(sizeof(raw->flags), &tmp);
pos += sizeof(raw->flags);
blk->len = cs_dsp_coeff_parse_int(sizeof(raw->len), &tmp);
break; break;
} }
@ -1224,6 +1300,8 @@ static inline void cs_dsp_coeff_parse_coeff(struct cs_dsp *dsp, const u8 **data,
cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags); cs_dsp_dbg(dsp, "\tCoefficient flags: %#x\n", blk->flags);
cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type); cs_dsp_dbg(dsp, "\tALSA control type: %#x\n", blk->ctl_type);
cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len); cs_dsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
return blk_end_pos;
} }
static int cs_dsp_check_coeff_flags(struct cs_dsp *dsp, 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,
struct cs_dsp_alg_region alg_region = {}; struct cs_dsp_alg_region alg_region = {};
struct cs_dsp_coeff_parsed_alg alg_blk; struct cs_dsp_coeff_parsed_alg alg_blk;
struct cs_dsp_coeff_parsed_coeff coeff_blk; struct cs_dsp_coeff_parsed_coeff coeff_blk;
const u8 *data = region->data; int i, pos, ret;
int i, ret;
pos = cs_dsp_coeff_parse_alg(dsp, region, &alg_blk);
if (pos < 0)
return pos;
cs_dsp_coeff_parse_alg(dsp, &data, &alg_blk);
for (i = 0; i < alg_blk.ncoeff; i++) { for (i = 0; i < alg_blk.ncoeff; i++) {
cs_dsp_coeff_parse_coeff(dsp, &data, &coeff_blk); pos = cs_dsp_coeff_parse_coeff(dsp, region, pos, &coeff_blk);
if (pos < 0)
return pos;
switch (coeff_blk.ctl_type) { switch (coeff_blk.ctl_type) {
case WMFW_CTL_TYPE_BYTES: case WMFW_CTL_TYPE_BYTES:
@ -1321,6 +1403,10 @@ static unsigned int cs_dsp_adsp1_parse_sizes(struct cs_dsp *dsp,
const struct wmfw_adsp1_sizes *adsp1_sizes; const struct wmfw_adsp1_sizes *adsp1_sizes;
adsp1_sizes = (void *)&firmware->data[pos]; adsp1_sizes = (void *)&firmware->data[pos];
if (sizeof(*adsp1_sizes) > firmware->size - pos) {
cs_dsp_err(dsp, "%s: file truncated\n", file);
return 0;
}
cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file, cs_dsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n", file,
le32_to_cpu(adsp1_sizes->dm), le32_to_cpu(adsp1_sizes->pm), 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,
const struct wmfw_adsp2_sizes *adsp2_sizes; const struct wmfw_adsp2_sizes *adsp2_sizes;
adsp2_sizes = (void *)&firmware->data[pos]; adsp2_sizes = (void *)&firmware->data[pos];
if (sizeof(*adsp2_sizes) > firmware->size - pos) {
cs_dsp_err(dsp, "%s: file truncated\n", file);
return 0;
}
cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file, cs_dsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n", file,
le32_to_cpu(adsp2_sizes->xm), le32_to_cpu(adsp2_sizes->ym), 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,
struct regmap *regmap = dsp->regmap; struct regmap *regmap = dsp->regmap;
unsigned int pos = 0; unsigned int pos = 0;
const struct wmfw_header *header; const struct wmfw_header *header;
const struct wmfw_adsp1_sizes *adsp1_sizes;
const struct wmfw_footer *footer; const struct wmfw_footer *footer;
const struct wmfw_region *region; const struct wmfw_region *region;
const struct cs_dsp_region *mem; const struct cs_dsp_region *mem;
@ -1392,10 +1481,8 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
ret = -EINVAL; ret = -EINVAL;
pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer); if (sizeof(*header) >= firmware->size) {
if (pos >= firmware->size) { ret = -EOVERFLOW;
cs_dsp_err(dsp, "%s: file too short, %zu bytes\n",
file, firmware->size);
goto out_fw; goto out_fw;
} }
@ -1423,22 +1510,36 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
pos = sizeof(*header); pos = sizeof(*header);
pos = dsp->ops->parse_sizes(dsp, file, pos, firmware); pos = dsp->ops->parse_sizes(dsp, file, pos, firmware);
if ((pos == 0) || (sizeof(*footer) > firmware->size - pos)) {
ret = -EOVERFLOW;
goto out_fw;
}
footer = (void *)&firmware->data[pos]; footer = (void *)&firmware->data[pos];
pos += sizeof(*footer); pos += sizeof(*footer);
if (le32_to_cpu(header->len) != pos) { if (le32_to_cpu(header->len) != pos) {
cs_dsp_err(dsp, "%s: unexpected header length %d\n", ret = -EOVERFLOW;
file, le32_to_cpu(header->len));
goto out_fw; goto out_fw;
} }
cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file, cs_dsp_dbg(dsp, "%s: timestamp %llu\n", file,
le64_to_cpu(footer->timestamp)); le64_to_cpu(footer->timestamp));
while (pos < firmware->size && while (pos < firmware->size) {
sizeof(*region) < firmware->size - pos) { /* Is there enough data for a complete block header? */
if (sizeof(*region) > firmware->size - pos) {
ret = -EOVERFLOW;
goto out_fw;
}
region = (void *)&(firmware->data[pos]); region = (void *)&(firmware->data[pos]);
if (le32_to_cpu(region->len) > firmware->size - pos - sizeof(*region)) {
ret = -EOVERFLOW;
goto out_fw;
}
region_name = "Unknown"; region_name = "Unknown";
reg = 0; reg = 0;
text = NULL; text = NULL;
@ -1495,16 +1596,6 @@ static int cs_dsp_load(struct cs_dsp *dsp, const struct firmware *firmware,
regions, le32_to_cpu(region->len), offset, regions, le32_to_cpu(region->len), offset,
region_name); region_name);
if (le32_to_cpu(region->len) >
firmware->size - pos - sizeof(*region)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, regions, region_name,
le32_to_cpu(region->len), firmware->size);
ret = -EINVAL;
goto out_fw;
}
if (text) { if (text) {
memcpy(text, region->data, le32_to_cpu(region->len)); memcpy(text, region->data, le32_to_cpu(region->len));
cs_dsp_info(dsp, "%s: %s\n", file, text); cs_dsp_info(dsp, "%s: %s\n", file, text);
@ -1555,6 +1646,9 @@ out_fw:
cs_dsp_buf_free(&buf_list); cs_dsp_buf_free(&buf_list);
kfree(text); kfree(text);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
return ret; return ret;
} }
@ -2122,10 +2216,20 @@ static int cs_dsp_load_coeff(struct cs_dsp *dsp, const struct firmware *firmware
pos = le32_to_cpu(hdr->len); pos = le32_to_cpu(hdr->len);
blocks = 0; blocks = 0;
while (pos < firmware->size && while (pos < firmware->size) {
sizeof(*blk) < firmware->size - pos) { /* Is there enough data for a complete block header? */
if (sizeof(*blk) > firmware->size - pos) {
ret = -EOVERFLOW;
goto out_fw;
}
blk = (void *)(&firmware->data[pos]); blk = (void *)(&firmware->data[pos]);
if (le32_to_cpu(blk->len) > firmware->size - pos - sizeof(*blk)) {
ret = -EOVERFLOW;
goto out_fw;
}
type = le16_to_cpu(blk->type); type = le16_to_cpu(blk->type);
offset = le16_to_cpu(blk->offset); offset = le16_to_cpu(blk->offset);
version = le32_to_cpu(blk->ver) >> 8; 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
} }
if (reg) { if (reg) {
if (le32_to_cpu(blk->len) >
firmware->size - pos - sizeof(*blk)) {
cs_dsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, blocks, region_name,
le32_to_cpu(blk->len),
firmware->size);
ret = -EINVAL;
goto out_fw;
}
buf = cs_dsp_buf_alloc(blk->data, buf = cs_dsp_buf_alloc(blk->data,
le32_to_cpu(blk->len), le32_to_cpu(blk->len),
&buf_list); &buf_list);
@ -2272,6 +2365,10 @@ out_fw:
regmap_async_complete(regmap); regmap_async_complete(regmap);
cs_dsp_buf_free(&buf_list); cs_dsp_buf_free(&buf_list);
kfree(text); kfree(text);
if (ret == -EOVERFLOW)
cs_dsp_err(dsp, "%s: file content overflows file data\n", file);
return ret; return ret;
} }

View file

@ -38,7 +38,9 @@ static bool rt711_readable_register(struct device *dev, unsigned int reg)
case 0x8300 ... 0x83ff: case 0x8300 ... 0x83ff:
case 0x9c00 ... 0x9cff: case 0x9c00 ... 0x9cff:
case 0xb900 ... 0xb9ff: case 0xb900 ... 0xb9ff:
case 0x752008:
case 0x752009: case 0x752009:
case 0x75200b:
case 0x752011: case 0x752011:
case 0x75201a: case 0x75201a:
case 0x752045: case 0x752045:

View file

@ -617,12 +617,6 @@ static int hda_dai_suspend(struct hdac_bus *bus)
sdai = swidget->private; sdai = swidget->private;
ops = sdai->platform_private; ops = sdai->platform_private;
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
cpu_dai);
if (ret < 0)
return ret;
/* for consistency with TRIGGER_SUSPEND */ /* for consistency with TRIGGER_SUSPEND */
if (ops->post_trigger) { if (ops->post_trigger) {
ret = ops->post_trigger(sdev, cpu_dai, ret = ops->post_trigger(sdev, cpu_dai,
@ -631,6 +625,12 @@ static int hda_dai_suspend(struct hdac_bus *bus)
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
ret = hda_link_dma_cleanup(hext_stream->link_substream,
hext_stream,
cpu_dai);
if (ret < 0)
return ret;
} }
} }

View file

@ -258,6 +258,12 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
snd_pcm_hw_constraint_integer(substream->runtime, snd_pcm_hw_constraint_integer(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIODS); SNDRV_PCM_HW_PARAM_PERIODS);
/* Limit the maximum number of periods to not exceed the BDL entries count */
if (runtime->hw.periods_max > HDA_DSP_MAX_BDL_ENTRIES)
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIODS,
runtime->hw.periods_min,
HDA_DSP_MAX_BDL_ENTRIES);
/* Only S16 and S32 supported by HDA hardware when used without DSP */ /* Only S16 and S32 supported by HDA hardware when used without DSP */
if (sdev->dspless_mode_selected) if (sdev->dspless_mode_selected)
snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT, snd_pcm_hw_constraint_mask64(substream->runtime, SNDRV_PCM_HW_PARAM_FORMAT,