Skip to content

Commit 22a1e77

Browse files
tiwaimchehab
authored andcommitted
xc2028: Fix use-after-free bug properly
The commit 8dfbcc4 ("[media] xc2028: avoid use after free") tried to address the reported use-after-free by clearing the reference. However, it's clearing the wrong pointer; it sets NULL to priv->ctrl.fname, but it's anyway overwritten by the next line memcpy(&priv->ctrl, p, sizeof(priv->ctrl)). OTOH, the actual code accessing the freed string is the strcmp() call with priv->fname: if (!firmware_name[0] && p->fname && priv->fname && strcmp(p->fname, priv->fname)) free_firmware(priv); where priv->fname points to the previous file name, and this was already freed by kfree(). For fixing the bug properly, this patch does the following: - Keep the copy of firmware file name in only priv->fname, priv->ctrl.fname isn't changed; - The allocation is done only when the firmware gets loaded; - The kfree() is called in free_firmware() commonly Fixes: commit 8dfbcc4 ('[media] xc2028: avoid use after free') Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent 9c76358 commit 22a1e77

File tree

1 file changed

+16
-21
lines changed

1 file changed

+16
-21
lines changed

drivers/media/tuners/tuner-xc2028.c

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,14 @@ static void free_firmware(struct xc2028_data *priv)
281281
int i;
282282
tuner_dbg("%s called\n", __func__);
283283

284+
/* free allocated f/w string */
285+
if (priv->fname != firmware_name)
286+
kfree(priv->fname);
287+
priv->fname = NULL;
288+
289+
priv->state = XC2028_NO_FIRMWARE;
290+
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
291+
284292
if (!priv->firm)
285293
return;
286294

@@ -291,9 +299,6 @@ static void free_firmware(struct xc2028_data *priv)
291299

292300
priv->firm = NULL;
293301
priv->firm_size = 0;
294-
priv->state = XC2028_NO_FIRMWARE;
295-
296-
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
297302
}
298303

299304
static int load_all_firmwares(struct dvb_frontend *fe,
@@ -884,9 +889,8 @@ static int check_firmware(struct dvb_frontend *fe, unsigned int type,
884889
return 0;
885890

886891
fail:
887-
priv->state = XC2028_NO_FIRMWARE;
892+
free_firmware(priv);
888893

889-
memset(&priv->cur_fw, 0, sizeof(priv->cur_fw));
890894
if (retry_count < 8) {
891895
msleep(50);
892896
retry_count++;
@@ -1332,11 +1336,8 @@ static int xc2028_dvb_release(struct dvb_frontend *fe)
13321336
mutex_lock(&xc2028_list_mutex);
13331337

13341338
/* only perform final cleanup if this is the last instance */
1335-
if (hybrid_tuner_report_instance_count(priv) == 1) {
1339+
if (hybrid_tuner_report_instance_count(priv) == 1)
13361340
free_firmware(priv);
1337-
kfree(priv->ctrl.fname);
1338-
priv->ctrl.fname = NULL;
1339-
}
13401341

13411342
if (priv)
13421343
hybrid_tuner_release_state(priv);
@@ -1399,19 +1400,8 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
13991400

14001401
/*
14011402
* Copy the config data.
1402-
* For the firmware name, keep a local copy of the string,
1403-
* in order to avoid troubles during device release.
14041403
*/
1405-
kfree(priv->ctrl.fname);
1406-
priv->ctrl.fname = NULL;
14071404
memcpy(&priv->ctrl, p, sizeof(priv->ctrl));
1408-
if (p->fname) {
1409-
priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL);
1410-
if (priv->ctrl.fname == NULL) {
1411-
rc = -ENOMEM;
1412-
goto unlock;
1413-
}
1414-
}
14151405

14161406
/*
14171407
* If firmware name changed, frees firmware. As free_firmware will
@@ -1426,10 +1416,15 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg)
14261416

14271417
if (priv->state == XC2028_NO_FIRMWARE) {
14281418
if (!firmware_name[0])
1429-
priv->fname = priv->ctrl.fname;
1419+
priv->fname = kstrdup(p->fname, GFP_KERNEL);
14301420
else
14311421
priv->fname = firmware_name;
14321422

1423+
if (!priv->fname) {
1424+
rc = -ENOMEM;
1425+
goto unlock;
1426+
}
1427+
14331428
rc = request_firmware_nowait(THIS_MODULE, 1,
14341429
priv->fname,
14351430
priv->i2c_props.adap->dev.parent,

0 commit comments

Comments
 (0)