| From d005f8c6588efcfbe88099b6edafc6f58c84a9c1 Mon Sep 17 00:00:00 2001 |
| From: Zhihao Cheng <chengzhihao1@huawei.com> |
| Date: Mon, 1 Jun 2020 17:12:31 +0800 |
| Subject: ubi: check kthread_should_stop() after the setting of task state |
| |
| From: Zhihao Cheng <chengzhihao1@huawei.com> |
| |
| commit d005f8c6588efcfbe88099b6edafc6f58c84a9c1 upstream. |
| |
| A detach hung is possible when a race occurs between the detach process |
| and the ubi background thread. The following sequences outline the race: |
| |
| ubi thread: if (list_empty(&ubi->works)... |
| |
| ubi detach: set_bit(KTHREAD_SHOULD_STOP, &kthread->flags) |
| => by kthread_stop() |
| wake_up_process() |
| => ubi thread is still running, so 0 is returned |
| |
| ubi thread: set_current_state(TASK_INTERRUPTIBLE) |
| schedule() |
| => ubi thread will never be scheduled again |
| |
| ubi detach: wait_for_completion() |
| => hung task! |
| |
| To fix that, we need to check kthread_should_stop() after we set the |
| task state, so the ubi thread will either see the stop bit and exit or |
| the task state is reset to runnable such that it isn't scheduled out |
| indefinitely. |
| |
| Signed-off-by: Zhihao Cheng <chengzhihao1@huawei.com> |
| Cc: <stable@vger.kernel.org> |
| Fixes: 801c135ce73d5df1ca ("UBI: Unsorted Block Images") |
| Reported-by: syzbot+853639d0cb16c31c7a14@syzkaller.appspotmail.com |
| Signed-off-by: Richard Weinberger <richard@nod.at> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/mtd/ubi/wl.c | 13 +++++++++++++ |
| 1 file changed, 13 insertions(+) |
| |
| --- a/drivers/mtd/ubi/wl.c |
| +++ b/drivers/mtd/ubi/wl.c |
| @@ -1639,6 +1639,19 @@ int ubi_thread(void *u) |
| !ubi->thread_enabled || ubi_dbg_is_bgt_disabled(ubi)) { |
| set_current_state(TASK_INTERRUPTIBLE); |
| spin_unlock(&ubi->wl_lock); |
| + |
| + /* |
| + * Check kthread_should_stop() after we set the task |
| + * state to guarantee that we either see the stop bit |
| + * and exit or the task state is reset to runnable such |
| + * that it's not scheduled out indefinitely and detects |
| + * the stop bit at kthread_should_stop(). |
| + */ |
| + if (kthread_should_stop()) { |
| + set_current_state(TASK_RUNNING); |
| + break; |
| + } |
| + |
| schedule(); |
| continue; |
| } |