aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorAnatolij Gustschin <agust@denx.de>2013-04-08 17:28:05 -0400
committerChris Ball <cjb@laptop.org>2013-04-12 15:13:13 -0400
commit70aa6109597ea6955a93f16430b588b5ee5ba547 (patch)
treee56a0940ff8fb9f27878466b6ed2d8692f93ca95 /drivers/mmc/host
parent7ff747c45908abb4fb894b21a8752a3e48acf02b (diff)
mmc: mxcmmc: fix race conditions for host->req and host->data access
mxcmci_dma_callback() is invoked by DMA drivers in soft-irq context and can be interrupted by the mxcmci_irq() interrupt which can finish the mmc request or data transfer and set host->req or host->data pointers to NULL. Then mxcmci_data_done() crashes with a null pointer dereferences. Protect all accesses to host->req and host->data by spin locks. Also check host->data pointer in mxcmci_watchdog() before dereferencing it. Signed-off-by: Anatolij Gustschin <agust@denx.de> Acked-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/mxcmmc.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 848ab2c2ff05..b82e37af3162 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -623,24 +623,40 @@ static void mxcmci_datawork(struct work_struct *work)
623 623
624static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) 624static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat)
625{ 625{
626 struct mmc_data *data = host->data; 626 struct mmc_request *req;
627 int data_error; 627 int data_error;
628 unsigned long flags;
629
630 spin_lock_irqsave(&host->lock, flags);
628 631
629 if (!data) 632 if (!host->data) {
633 spin_unlock_irqrestore(&host->lock, flags);
630 return; 634 return;
635 }
636
637 if (!host->req) {
638 spin_unlock_irqrestore(&host->lock, flags);
639 return;
640 }
641
642 req = host->req;
643 if (!req->stop)
644 host->req = NULL; /* we will handle finish req below */
631 645
632 data_error = mxcmci_finish_data(host, stat); 646 data_error = mxcmci_finish_data(host, stat);
633 647
648 spin_unlock_irqrestore(&host->lock, flags);
649
634 mxcmci_read_response(host, stat); 650 mxcmci_read_response(host, stat);
635 host->cmd = NULL; 651 host->cmd = NULL;
636 652
637 if (host->req->stop) { 653 if (req->stop) {
638 if (mxcmci_start_cmd(host, host->req->stop, 0)) { 654 if (mxcmci_start_cmd(host, req->stop, 0)) {
639 mxcmci_finish_request(host, host->req); 655 mxcmci_finish_request(host, req);
640 return; 656 return;
641 } 657 }
642 } else { 658 } else {
643 mxcmci_finish_request(host, host->req); 659 mxcmci_finish_request(host, req);
644 } 660 }
645} 661}
646 662
@@ -931,7 +947,8 @@ static void mxcmci_watchdog(unsigned long data)
931 947
932 /* Mark transfer as erroneus and inform the upper layers */ 948 /* Mark transfer as erroneus and inform the upper layers */
933 949
934 host->data->error = -ETIMEDOUT; 950 if (host->data)
951 host->data->error = -ETIMEDOUT;
935 host->req = NULL; 952 host->req = NULL;
936 host->cmd = NULL; 953 host->cmd = NULL;
937 host->data = NULL; 954 host->data = NULL;