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

Commit 84bac424 authored by Sujit Reddy Thumma's avatar Sujit Reddy Thumma Committed by Matt Wagantall
Browse files

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] [<c0339cd4>] (do_raw_spin_lock+0x8/0x13c) from
[   30.118418] [<c095d178>] (_raw_spin_lock_irqsave+0x20/0x28) from
[   30.127445] [<c0142ef4>] (__wake_up+0x20/0x50) from
[   30.136124] [<c0663f70>] (mmc_request_done+0x30c/0x368) from
[   30.145932] [<c067bd98>] (sdhci_tasklet_finish+0x130/0x13c) from

Change-Id: I9a21431b5fd9bb9bbcb5c18a9895096fe845e64b
Signed-off-by: default avatarSujit Reddy Thumma <sthumma@codeaurora.org>
[merez@codeaurora.org: fixed conflicts due to missing stop transmission]
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
parent 95b3a8d4
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -403,8 +403,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);
	mrq->host->context_info.is_done_rcv = true;
	wake_up_interruptible(&mrq->host->context_info.wait);
	spin_unlock_irqrestore(&context_info->lock, flags);
}

static void mmc_wait_done(struct mmc_request *mrq)
@@ -465,6 +470,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) {
@@ -472,9 +478,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;