Skip to content

Commit

Permalink
writeback: fix memory leak in wb_queue_work()
Browse files Browse the repository at this point in the history
[ Upstream commit 4a3a485 ]

When WB_registered flag is not set, wb_queue_work() skips queuing the
work, but does not perform the necessary clean up. In particular, if
work->auto_free is true, it should free the memory.

The leak condition can be reprouced by following these steps:

   mount /dev/sdb /mnt/sdb
   /* In qemu console: device_del sdb */
   umount /dev/sdb

Above will result in a wb_queue_work() call on an unregistered wb and
thus leak memory.

Reported-by: John Sperbeck <[email protected]>
Signed-off-by: Tahsin Erdogan <[email protected]>
Reviewed-by: Jan Kara <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
Tahsin Erdogan authored and gregkh committed Dec 20, 2017
1 parent fbdf477 commit b424289
Showing 1 changed file with 21 additions and 14 deletions.
35 changes: 21 additions & 14 deletions fs/fs-writeback.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,19 +173,33 @@ static void wb_wakeup(struct bdi_writeback *wb)
spin_unlock_bh(&wb->work_lock);
}

static void finish_writeback_work(struct bdi_writeback *wb,
struct wb_writeback_work *work)
{
struct wb_completion *done = work->done;

if (work->auto_free)
kfree(work);
if (done && atomic_dec_and_test(&done->cnt))
wake_up_all(&wb->bdi->wb_waitq);
}

static void wb_queue_work(struct bdi_writeback *wb,
struct wb_writeback_work *work)
{
trace_writeback_queue(wb, work);

spin_lock_bh(&wb->work_lock);
if (!test_bit(WB_registered, &wb->state))
goto out_unlock;
if (work->done)
atomic_inc(&work->done->cnt);
list_add_tail(&work->list, &wb->work_list);
mod_delayed_work(bdi_wq, &wb->dwork, 0);
out_unlock:

spin_lock_bh(&wb->work_lock);

if (test_bit(WB_registered, &wb->state)) {
list_add_tail(&work->list, &wb->work_list);
mod_delayed_work(bdi_wq, &wb->dwork, 0);
} else
finish_writeback_work(wb, work);

spin_unlock_bh(&wb->work_lock);
}

Expand Down Expand Up @@ -1839,16 +1853,9 @@ static long wb_do_writeback(struct bdi_writeback *wb)

set_bit(WB_writeback_running, &wb->state);
while ((work = get_next_work_item(wb)) != NULL) {
struct wb_completion *done = work->done;

trace_writeback_exec(wb, work);

wrote += wb_writeback(wb, work);

if (work->auto_free)
kfree(work);
if (done && atomic_dec_and_test(&done->cnt))
wake_up_all(&wb->bdi->wb_waitq);
finish_writeback_work(wb, work);
}

/*
Expand Down

0 comments on commit b424289

Please sign in to comment.