possible use-after-free in drivers/mtd/ubi/wl.c: erase_worker
Subsystems (2)
Hi,
I think I found a potential problem in drivers/mtd/ubi/wl.c in erase_worker():
1050 ubi_err("failed to erase PEB %d, error %d", pnum, err);
1051 kfree(wl_wrk);
1052 kmem_cache_free(ubi_wl_entry_slab, e);1053
1054 if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
1055 err == -EBUSY) {
1056 int err1;
1057
1058 /* Re-schedule the LEB for erasure */
1059 err1 = schedule_erase(ubi, e, 0);
The pointer e is freed at line 1052 (kmem_cache_free), but later it is passed to schedule_erase which will eventually call erase_worker where it will be dereferenced and/or freed again.
It seems to have been introduced in commit784c145444e7dd58ae740d406155b72ac658f151
Emese
Quoted 16 lines · Emese Revfy
Hi,
I think I found a potential problem in
drivers/mtd/ubi/wl.cinerase_worker():1050
ubi_err("failed to erase PEB %d, error %d", pnum, err); 1051kfree(wl_wrk);
1052kmem_cache_free(ubi_wl_entry_slab, e);
1053
1054 if (err ==-EINTR|| err ==-ENOMEM|| err ==-EAGAIN|| 1055 err ==-EBUSY) {
1056 int err1;
1057
1058 /* Re-schedule the LEB for erasure */ 1059 err1 =schedule_erase(ubi, e, 0);
Quoted 3 lines
The pointer e is freed at line 1052 (
kmem_cache_free), but later it is passed toschedule_erasewhich will eventually callerase_workerwhere it will be dereferenced and/or freed again.
Yes. That does seem to be a problem. Care to create a patch that solves this by deferring the frees until after the err variable is checked?
josh
Well-spotted, thanks. I've just pushed this patch to the UBIFS git tree:
From: Artem Bityutskiy <[email protected]>
Subject: [PATCH] UBI: fix use-after-free on error path
When we fail to erase a PEB, we free the corresponding erase entry object, but then re-schedule this object if the error code was something like -EAGAIN. Obviously, it is a bug to use the object after we have freed it.
--- drivers/mtd/ubi/wl.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-)
drivers/mtd/ubi/wl.c +4 -3
@@ -1049,7 +1049,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,ubi_err ("failed to erase PEB %d, error %d" , pnum, err);kfree (wl_wrk); -kmem_cache_free (ubi_wl_entry_slab, e);if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || err == -EBUSY ) {
@@ -1062,14 +1061,16 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, goto out_ro; }return err; - }else if (err != -EIO ) { + } + +kmem_cache_free (ubi_wl_entry_slab, e); +if (err != -EIO )/* * If this is not %-EIO, we have no idea what to do. Scheduling * this physical eraseblock for erasure again would cause * errors again and again. Well, lets switch to R/O mode. */ goto out_ro; - }/* It is %-EIO, the PEB went bad */
Signature · 5 lines
1.7.7.3 -- Best Regards, Artem Bityutskiy