Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 56248b49 authored by Sebastian Siewior's avatar Sebastian Siewior Committed by Nikhilesh Reddy
Browse files

mtd: ubi: fixup error correction in do_sync_erase()



Since fastmap we gained do_sync_erase(). This function can return an error
and its error handling isn't obvious. First the memory allocation for
struct ubi_work can fail and as such struct ubi_wl_entry is leaked.
However if the memory allocation succeeds then the tail function takes
care of the struct ubi_wl_entry. A free here could result in a double
free.
To make the error handling simpler, I split the tail function into one
piece which does the work and another which frees the struct ubi_work
which is passed as argument. As result do_sync_erase() can keep the
struct on stack and we get rid of one error source.

CRs-Fixed: 975289
Change-Id: Ie584b80b4e2c9efa63e2cfb0df79598df50b17e2
Cc: <stable@vger.kernel.org>
Fixes: 8199b901 ("UBI: Add fastmap support to the WL sub-system")
Signed-off-by: default avatarSebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
Git-commit: 1a31b20cd81d5cbc7ec6e24cb08066009a1ca32d
Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git


Signed-off-by: default avatarNikhilesh Reddy <reddyn@codeaurora.org>
parent bb1355ed
Loading
Loading
Loading
Loading
+28 −24
Original line number Diff line number Diff line
@@ -622,6 +622,7 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
	return 0;
}

static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk);
/**
 * do_sync_erase - run the erase worker synchronously.
 * @ubi: UBI device description object
@@ -634,20 +635,16 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e,
			 int vol_id, int lnum, int torture)
{
	struct ubi_work *wl_wrk;
	struct ubi_work wl_wrk;

	dbg_wl("sync erase of PEB %i", e->pnum);

	wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS);
	if (!wl_wrk)
		return -ENOMEM;

	wl_wrk->e = e;
	wl_wrk->vol_id = vol_id;
	wl_wrk->lnum = lnum;
	wl_wrk->torture = torture;
	wl_wrk.e = e;
	wl_wrk.vol_id = vol_id;
	wl_wrk.lnum = lnum;
	wl_wrk.torture = torture;

	return erase_worker(ubi, wl_wrk, 0);
	return __erase_worker(ubi, &wl_wrk);
}

/**
@@ -1043,7 +1040,7 @@ out_unlock:
}

/**
 * erase_worker - physical eraseblock erase worker function.
 * __erase_worker - physical eraseblock erase worker function.
 * @ubi: UBI device description object
 * @wl_wrk: the work object
 * @shutdown: non-zero if the worker has to free memory and exit
@@ -1054,8 +1051,7 @@ out_unlock:
 * needed. Returns zero in case of success and a negative error code in case of
 * failure.
 */
static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
			int shutdown)
static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)
{
	struct ubi_wl_entry *e = wl_wrk->e;
	int pnum = e->pnum;
@@ -1063,21 +1059,11 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
	int lnum = wl_wrk->lnum;
	int err, available_consumed = 0;

	if (shutdown) {
		dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec);
		kfree(wl_wrk);
		wl_entry_destroy(ubi, e);
		return 0;
	}

	dbg_wl("erase PEB %d EC %d LEB %d:%d",
	       pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum);

	err = sync_erase(ubi, e, wl_wrk->torture);
	if (!err) {
		/* Fine, we've erased it successfully */
		kfree(wl_wrk);

		spin_lock(&ubi->wl_lock);
		wl_tree_add(e, &ubi->free);
		ubi->free_count++;
@@ -1095,7 +1081,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
	}

	ubi_err(ubi, "failed to erase PEB %d, error %d", pnum, err);
	kfree(wl_wrk);

	if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
	    err == -EBUSY) {
@@ -1179,6 +1164,25 @@ out_ro:
	return err;
}

static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
			  int shutdown)
{
	int ret;

	if (shutdown) {
		struct ubi_wl_entry *e = wl_wrk->e;

		dbg_wl("cancel erasure of PEB %d EC %d", e->pnum, e->ec);
		kfree(wl_wrk);
		wl_entry_destroy(ubi, e);
		return 0;
	}

	ret = __erase_worker(ubi, wl_wrk);
	kfree(wl_wrk);
	return ret;
}

/**
 * ubi_wl_put_peb - return a PEB to the wear-leveling sub-system.
 * @ubi: UBI device description object