Commit 5d542b85 authored by Stefan Binding's avatar Stefan Binding Committed by Takashi Iwai

ALSA: hda: cs35l41: Cleanup and fix double free in firmware request

There is an unlikely but possible double free when loading firmware,
and a missing free calls if a firmware is successfully requested but
the coefficient file request fails, leading to the fallback firmware
request occurring without clearing the previously loaded firmware.

Fixes: cd40dad2 ("ALSA: hda: cs35l41: Ensure firmware/tuning pairs are always loaded")
Reported-by: default avatarkernel test robot <lkp@intel.com>
Reported-by: default avatarDan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/r/202309291331.0JUUQnPT-lkp@intel.com/Signed-off-by: default avatarStefan Binding <sbinding@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20231003142138.180108-1-sbinding@opensource.cirrus.comSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 9c1a3f43
...@@ -185,10 +185,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, ...@@ -185,10 +185,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->speaker_id, "wmfw"); cs35l41->speaker_id, "wmfw");
if (!ret) { if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
CS35L41_FIRMWARE_ROOT, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "bin"); cs35l41->speaker_id, "bin");
if (ret)
goto coeff_err;
return 0;
} }
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
...@@ -197,10 +201,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, ...@@ -197,10 +201,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, -1, "wmfw"); cs35l41->amp_name, -1, "wmfw");
if (!ret) { if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */ /* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename, ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
CS35L41_FIRMWARE_ROOT, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->acpi_subsystem_id, cs35l41->amp_name,
cs35l41->speaker_id, "bin"); cs35l41->speaker_id, "bin");
if (ret)
goto coeff_err;
return 0;
} }
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
...@@ -215,10 +223,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, ...@@ -215,10 +223,14 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, cs35l41->speaker_id, "bin"); cs35l41->amp_name, cs35l41->speaker_id, "bin");
if (ret) if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
return cs35l41_request_firmware_file(cs35l41, coeff_firmware, ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
coeff_filename, CS35L41_FIRMWARE_ROOT, coeff_filename, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, NULL, cs35l41->acpi_subsystem_id, NULL,
cs35l41->speaker_id, "bin"); cs35l41->speaker_id, "bin");
if (ret)
goto coeff_err;
return 0;
} }
/* try cirrus/part-dspN-fwtype-sub.wmfw */ /* try cirrus/part-dspN-fwtype-sub.wmfw */
...@@ -233,12 +245,50 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41, ...@@ -233,12 +245,50 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->speaker_id, "bin"); cs35l41->speaker_id, "bin");
if (ret) if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */ /* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
return cs35l41_request_firmware_file(cs35l41, coeff_firmware, ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware,
coeff_filename, CS35L41_FIRMWARE_ROOT, coeff_filename, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, NULL, cs35l41->acpi_subsystem_id, NULL,
cs35l41->speaker_id, "bin"); cs35l41->speaker_id, "bin");
if (ret)
goto coeff_err;
}
return ret;
coeff_err:
release_firmware(*wmfw_firmware);
kfree(*wmfw_filename);
return ret;
}
static int cs35l41_fallback_firmware_file(struct cs35l41_hda *cs35l41,
const struct firmware **wmfw_firmware,
char **wmfw_filename,
const struct firmware **coeff_firmware,
char **coeff_filename)
{
int ret;
/* Handle fallback */
dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
/* fallback try cirrus/part-dspN-fwtype.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
if (ret)
goto err;
/* fallback try cirrus/part-dspN-fwtype.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
if (ret) {
release_firmware(*wmfw_firmware);
kfree(*wmfw_filename);
goto err;
} }
return 0;
err:
dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
return ret; return ret;
} }
...@@ -254,7 +304,6 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, ...@@ -254,7 +304,6 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename, ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename); coeff_firmware, coeff_filename);
goto out; goto out;
} }
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */ /* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
...@@ -267,6 +316,9 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, ...@@ -267,6 +316,9 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
CS35L41_FIRMWARE_ROOT, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, cs35l41->amp_name, cs35l41->acpi_subsystem_id, cs35l41->amp_name,
-1, "bin"); -1, "bin");
if (ret)
goto coeff_err;
goto out; goto out;
} }
...@@ -286,32 +338,23 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41, ...@@ -286,32 +338,23 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
CS35L41_FIRMWARE_ROOT, CS35L41_FIRMWARE_ROOT,
cs35l41->acpi_subsystem_id, NULL, -1, cs35l41->acpi_subsystem_id, NULL, -1,
"bin"); "bin");
if (ret)
goto coeff_err;
} }
out: out:
if (!ret) if (ret)
return 0; /* if all attempts at finding firmware fail, try fallback */
goto fallback;
/* Handle fallback */ return 0;
dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
coeff_err:
release_firmware(*wmfw_firmware); release_firmware(*wmfw_firmware);
kfree(*wmfw_filename); kfree(*wmfw_filename);
fallback:
/* fallback try cirrus/part-dspN-fwtype.wmfw */ return cs35l41_fallback_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename, coeff_firmware, coeff_filename);
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
if (!ret)
/* fallback try cirrus/part-dspN-fwtype.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
if (ret) {
release_firmware(*wmfw_firmware);
kfree(*wmfw_filename);
dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
}
return ret;
} }
#if IS_ENABLED(CONFIG_EFI) #if IS_ENABLED(CONFIG_EFI)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment