From c75b73aff388cd297fa0e85f89eb575acf16873c Mon Sep 17 00:00:00 2001 From: Sujit Reddy Thumma Date: Tue, 9 Dec 2014 20:44:41 +0200 Subject: mmc: core: Fix null pointer dereference due to illegal mmc request Fix a race condition that can lead to null pointer dereference while the MMC transfers are going on. 1) mmc_request_done() -> mmc_wait_for_data_done -> step1: update is_done_rcv step2: wake_up sleeping thread (mmcqd) waiting for is_done_rcv 2) mmcqd -> mmc_wait_for_data_req_done step4: wait for is_done_rcv or is_new_req step5: is_new_req set from block layer context and mmcqd is woken up step6: let's say step1 is done, so complete the current request step7: fetch new request and issue to host layer step8: fetch one more request and wait for previous request to complete In the above execution contexts, if step4-step8 happens between step1 and step2 a null pointer dereference is observed - [ 29.483302] Unable to handle kernel NULL pointer dereference at virtual address 00000488 [ 29.490366] pgd = c0004000 [ 29.493054] [00000488] *pgd=00000000 [ 29.518937] PC is at do_raw_spin_lock+0x8/0x13c [ 29.523445] LR is at _raw_spin_lock_irqsave+0x20/0x28 [ 30.108789] [] (do_raw_spin_lock+0x8/0x13c) from [ 30.118418] [] (_raw_spin_lock_irqsave+0x20/0x28) from [ 30.127445] [] (__wake_up+0x20/0x50) from [ 30.136124] [] (mmc_request_done+0x30c/0x368) from [ 30.145932] [] (sdhci_tasklet_finish+0x130/0x13c) from Change-Id: I9a21431b5fd9bb9bbcb5c18a9895096fe845e64b Signed-off-by: Sujit Reddy Thumma [merez@codeaurora.org: fixed conflicts due to missing stop transmission] Signed-off-by: Maya Erez [subhashj@codeaurora.org: fixed trivial merge conflicts] Signed-off-by: Subhash Jadavani --- drivers/mmc/core/core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 188ace67504b..9e60bd2d021c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -463,10 +463,13 @@ EXPORT_SYMBOL(mmc_start_bkops); */ static void mmc_wait_data_done(struct mmc_request *mrq) { + unsigned long flags; struct mmc_context_info *context_info = &mrq->host->context_info; + spin_lock_irqsave(&context_info->lock, flags); context_info->is_done_rcv = true; wake_up_interruptible(&context_info->wait); + spin_unlock_irqrestore(&context_info->lock, flags); } static void mmc_wait_done(struct mmc_request *mrq) @@ -532,6 +535,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, struct mmc_command *cmd; struct mmc_context_info *context_info = &host->context_info; int err; + bool is_done_rcv = false; unsigned long flags; while (1) { @@ -539,9 +543,10 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, (context_info->is_done_rcv || context_info->is_new_req)); spin_lock_irqsave(&context_info->lock, flags); + is_done_rcv = context_info->is_done_rcv; context_info->is_waiting_last_req = false; spin_unlock_irqrestore(&context_info->lock, flags); - if (context_info->is_done_rcv) { + if (is_done_rcv) { context_info->is_done_rcv = false; context_info->is_new_req = false; cmd = mrq->cmd; -- cgit v1.2.3