소스 검색

ASoC: wm2000: Fix use-after-free - don't release_firmware() twice on error

In wm2000_i2c_probe(), if we take the true branch in

"
  ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000,
                               NULL, 0);
  if (ret != 0)
          goto err_fw;
"

then we'll release_firmware(fw) at the 'err_fw' label. But we've already
done that just a few lines above. That's a use-after-free bug.

This patch restructures the code so that we always call
release_firmware(fw) before leaving the function, but only ever call
it once.
This means that we have to initialize 'fw' to NULL since some paths
may now end up calling it without having called request_firmware(),
but since request_firmware() deals gracefully with NULL pointers, we
are fine if we just NULL initialize it.

Signed-off-by: Jesper Juhl <jj@chaosbits.net>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Jesper Juhl 13 년 전
부모
커밋
c83f1d7e71
1개의 변경된 파일13개의 추가작업 그리고 18개의 파일을 삭제
  1. 13 18
      sound/soc/codecs/wm2000.c

+ 13 - 18
sound/soc/codecs/wm2000.c

@@ -733,8 +733,9 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 	struct wm2000_priv *wm2000;
 	struct wm2000_priv *wm2000;
 	struct wm2000_platform_data *pdata;
 	struct wm2000_platform_data *pdata;
 	const char *filename;
 	const char *filename;
-	const struct firmware *fw;
-	int reg, ret;
+	const struct firmware *fw = NULL;
+	int ret;
+	int reg;
 	u16 id;
 	u16 id;
 
 
 	wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv),
 	wm2000 = devm_kzalloc(&i2c->dev, sizeof(struct wm2000_priv),
@@ -751,7 +752,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 		ret = PTR_ERR(wm2000->regmap);
 		ret = PTR_ERR(wm2000->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
 			ret);
 			ret);
-		goto err;
+		goto out;
 	}
 	}
 
 
 	/* Verify that this is a WM2000 */
 	/* Verify that this is a WM2000 */
@@ -763,7 +764,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 	if (id != 0x2000) {
 	if (id != 0x2000) {
 		dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
 		dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
 		ret = -ENODEV;
 		ret = -ENODEV;
-		goto err_regmap;
+		goto out_regmap_exit;
 	}
 	}
 
 
 	reg = wm2000_read(i2c, WM2000_REG_REVISON);
 	reg = wm2000_read(i2c, WM2000_REG_REVISON);
@@ -782,7 +783,7 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 	ret = request_firmware(&fw, filename, &i2c->dev);
 	ret = request_firmware(&fw, filename, &i2c->dev);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
 		dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
-		goto err_regmap;
+		goto out_regmap_exit;
 	}
 	}
 
 
 	/* Pre-cook the concatenation of the register address onto the image */
 	/* Pre-cook the concatenation of the register address onto the image */
@@ -793,15 +794,13 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 	if (wm2000->anc_download == NULL) {
 	if (wm2000->anc_download == NULL) {
 		dev_err(&i2c->dev, "Out of memory\n");
 		dev_err(&i2c->dev, "Out of memory\n");
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto err_fw;
+		goto out_regmap_exit;
 	}
 	}
 
 
 	wm2000->anc_download[0] = 0x80;
 	wm2000->anc_download[0] = 0x80;
 	wm2000->anc_download[1] = 0x00;
 	wm2000->anc_download[1] = 0x00;
 	memcpy(wm2000->anc_download + 2, fw->data, fw->size);
 	memcpy(wm2000->anc_download + 2, fw->data, fw->size);
 
 
-	release_firmware(fw);
-
 	wm2000->anc_eng_ena = 1;
 	wm2000->anc_eng_ena = 1;
 	wm2000->anc_active = 1;
 	wm2000->anc_active = 1;
 	wm2000->spk_ena = 1;
 	wm2000->spk_ena = 1;
@@ -809,18 +808,14 @@ static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
 
 
 	wm2000_reset(wm2000);
 	wm2000_reset(wm2000);
 
 
-	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000,
-				     NULL, 0);
-	if (ret != 0)
-		goto err_fw;
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm2000, NULL, 0);
+	if (!ret)
+		goto out;
 
 
-	return 0;
-
-err_fw:
-	release_firmware(fw);
-err_regmap:
+out_regmap_exit:
 	regmap_exit(wm2000->regmap);
 	regmap_exit(wm2000->regmap);
-err:
+out:
+	release_firmware(fw);
 	return ret;
 	return ret;
 }
 }