diff options
author | Anatolij Gustschin <agust@denx.de> | 2013-04-08 17:28:05 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2013-04-12 15:13:13 -0400 |
commit | 70aa6109597ea6955a93f16430b588b5ee5ba547 (patch) | |
tree | e56a0940ff8fb9f27878466b6ed2d8692f93ca95 /drivers/mmc/host | |
parent | 7ff747c45908abb4fb894b21a8752a3e48acf02b (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.c | 31 |
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 | ||
624 | static void mxcmci_data_done(struct mxcmci_host *host, unsigned int stat) | 624 | static 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; |