diff --git a/kernel.spec b/kernel.spec index 5b0a7b31b..e3f07ed3a 100644 --- a/kernel.spec +++ b/kernel.spec @@ -647,6 +647,9 @@ Patch512: ext4-dont-manipulate-recovery-flag-when-freezing.patch #CVE-2015-6666 rhbz 1256746 1256753 Patch513: Revert-sched-x86_64-Don-t-save-flags-on-context-swit.patch +#rhbz 1256281 +Patch26266: mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch + # END OF PATCH DEFINITIONS %endif @@ -1399,6 +1402,9 @@ ApplyPatch ext4-dont-manipulate-recovery-flag-when-freezing.patch #CVE-2015-6666 rhbz 1256746 1256753 ApplyPatch Revert-sched-x86_64-Don-t-save-flags-on-context-swit.patch +#rhbz 1256281 +ApplyPatch mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch + # END OF PATCH APPLICATIONS %endif @@ -2258,6 +2264,9 @@ fi # ||----w | # || || %changelog +* Tue Aug 25 2015 Laura Abbott +- Fix DMA leak from sdhci (rhbz 1256281) + * Tue Aug 25 2015 Josh Boyer - CVE-2015-6666 x86_64 NT flag handling DoS (rhbz 1256746 1256753) diff --git a/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch b/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch new file mode 100644 index 000000000..99cb4587a --- /dev/null +++ b/mmc-sdhci-fix-dma-memory-leak-in-sdhci_pre_req.patch @@ -0,0 +1,207 @@ +From dfc9fad3e582c41b7e7cedc4d7fbbde6942fdfed Mon Sep 17 00:00:00 2001 +From: Haibo Chen +Date: Tue, 25 Aug 2015 10:02:11 +0800 +Subject: [PATCH] mmc: sdhci: fix dma memory leak in sdhci_pre_req() + +Currently one mrq->data maybe execute dma_map_sg() twice +when mmc subsystem prepare over one new request, and the +following log show up: + sdhci[sdhci_pre_dma_transfer] invalid cookie: 24, next-cookie 25 + +In this condition, mrq->date map a dma-memory(1) in sdhci_pre_req +for the first time, and map another dma-memory(2) in sdhci_prepare_data +for the second time. But driver only unmap the dma-memory(2), and +dma-memory(1) never unmapped, which cause the dma memory leak issue. + +This patch use another method to map the dma memory for the mrq->data +which can fix this dma memory leak issue. + +Fixes: commit 348487cb28e66b0 ("mmc: sdhci: use pipeline mmc requests to improve performance") +Cc: stable@vger.kernel.org # 4.0+ +Reported-and-tested-by: Jiri Slaby +Signed-off-by: Haibo Chen +--- + drivers/mmc/host/sdhci.c | 67 ++++++++++++++++++------------------------------ + drivers/mmc/host/sdhci.h | 8 +++--- + 2 files changed, 29 insertions(+), 46 deletions(-) + +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index bec8a30..ef5b604 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -55,8 +55,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); + static void sdhci_tuning_timer(unsigned long data); + static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); + static int sdhci_pre_dma_transfer(struct sdhci_host *host, +- struct mmc_data *data, +- struct sdhci_host_next *next); ++ struct mmc_data *data); + static int sdhci_do_get_cd(struct sdhci_host *host); + + #ifdef CONFIG_PM +@@ -510,7 +509,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, + goto fail; + BUG_ON(host->align_addr & host->align_mask); + +- host->sg_count = sdhci_pre_dma_transfer(host, data, NULL); ++ host->sg_count = sdhci_pre_dma_transfer(host, data); + if (host->sg_count < 0) + goto unmap_align; + +@@ -649,9 +648,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host, + } + } + +- if (!data->host_cookie) ++ if (data->host_cookie == COOKIE_MAPPED) { + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, direction); ++ data->host_cookie = COOKIE_UNMAPPED; ++ } + } + + static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) +@@ -847,7 +848,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) + } else { + int sg_cnt; + +- sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); ++ sg_cnt = sdhci_pre_dma_transfer(host, data); + if (sg_cnt <= 0) { + /* + * This only happens when someone fed +@@ -963,11 +964,13 @@ static void sdhci_finish_data(struct sdhci_host *host) + if (host->flags & SDHCI_USE_ADMA) + sdhci_adma_table_post(host, data); + else { +- if (!data->host_cookie) ++ if (data->host_cookie == COOKIE_MAPPED) { + dma_unmap_sg(mmc_dev(host->mmc), + data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); ++ data->host_cookie = COOKIE_UNMAPPED; ++ } + } + } + +@@ -2129,49 +2132,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + struct mmc_data *data = mrq->data; + + if (host->flags & SDHCI_REQ_USE_DMA) { +- if (data->host_cookie) ++ if (data->host_cookie == COOKIE_GIVEN || ++ data->host_cookie == COOKIE_MAPPED) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); +- mrq->data->host_cookie = 0; ++ data->host_cookie = COOKIE_UNMAPPED; + } + } + + static int sdhci_pre_dma_transfer(struct sdhci_host *host, +- struct mmc_data *data, +- struct sdhci_host_next *next) ++ struct mmc_data *data) + { + int sg_count; + +- if (!next && data->host_cookie && +- data->host_cookie != host->next_data.cookie) { +- pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n", +- __func__, data->host_cookie, host->next_data.cookie); +- data->host_cookie = 0; ++ if (data->host_cookie == COOKIE_MAPPED) { ++ data->host_cookie = COOKIE_GIVEN; ++ return data->sg_count; + } + +- /* Check if next job is already prepared */ +- if (next || +- (!next && data->host_cookie != host->next_data.cookie)) { +- sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, +- data->sg_len, +- data->flags & MMC_DATA_WRITE ? +- DMA_TO_DEVICE : DMA_FROM_DEVICE); +- +- } else { +- sg_count = host->next_data.sg_count; +- host->next_data.sg_count = 0; +- } ++ WARN_ON(data->host_cookie == COOKIE_GIVEN); + ++ sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, ++ data->flags & MMC_DATA_WRITE ? ++ DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (sg_count == 0) +- return -EINVAL; ++ return -ENOSPC; + +- if (next) { +- next->sg_count = sg_count; +- data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; +- } else +- host->sg_count = sg_count; ++ data->sg_count = sg_count; ++ data->host_cookie = COOKIE_MAPPED; + + return sg_count; + } +@@ -2181,16 +2171,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, + { + struct sdhci_host *host = mmc_priv(mmc); + +- if (mrq->data->host_cookie) { +- mrq->data->host_cookie = 0; +- return; +- } ++ mrq->data->host_cookie = COOKIE_UNMAPPED; + + if (host->flags & SDHCI_REQ_USE_DMA) +- if (sdhci_pre_dma_transfer(host, +- mrq->data, +- &host->next_data) < 0) +- mrq->data->host_cookie = 0; ++ sdhci_pre_dma_transfer(host, mrq->data); + } + + static void sdhci_card_event(struct mmc_host *mmc) +@@ -3088,7 +3072,6 @@ int sdhci_add_host(struct sdhci_host *host) + host->max_clk = host->ops->get_max_clock(host); + } + +- host->next_data.cookie = 1; + /* + * In case of Host Controller v3.00, find out whether clock + * multiplier is supported. +diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h +index e639b7f..eea23f6 100644 +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc { + */ + #define SDHCI_MAX_SEGS 128 + +-struct sdhci_host_next { +- unsigned int sg_count; +- s32 cookie; ++enum sdhci_cookie { ++ COOKIE_UNMAPPED, ++ COOKIE_MAPPED, ++ COOKIE_GIVEN, + }; + + struct sdhci_host { +@@ -506,7 +507,6 @@ struct sdhci_host { + #define SDHCI_TUNING_MODE_1 0 + struct timer_list tuning_timer; /* Timer for tuning */ + +- struct sdhci_host_next next_data; + unsigned long private[0] ____cacheline_aligned; + }; + +-- +2.4.3 +