summaryrefslogtreecommitdiffstats
path: root/0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch
diff options
context:
space:
mode:
Diffstat (limited to '0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch')
-rw-r--r--0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch136
1 files changed, 136 insertions, 0 deletions
diff --git a/0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch b/0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch
new file mode 100644
index 000000000..873509eb0
--- /dev/null
+++ b/0001-driver-code-clarify-and-fix-platform-device-DMA-mask.patch
@@ -0,0 +1,136 @@
+From e3a36eb6dfaeea8175c05d5915dcf0b939be6dab Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Wed, 11 Mar 2020 17:07:10 +0100
+Subject: [PATCH] driver code: clarify and fix platform device DMA mask
+ allocation
+
+This does three inter-related things to clarify the usage of the
+platform device dma_mask field. In the process, fix the bug introduced
+by cdfee5623290 ("driver core: initialize a default DMA mask for
+platform device") that caused Artem Tashkinov's laptop to not boot with
+newer Fedora kernels.
+
+This does:
+
+ - First off, rename the field to "platform_dma_mask" to make it
+ greppable.
+
+ We have way too many different random fields called "dma_mask" in
+ various data structures, where some of them are actual masks, and
+ some of them are just pointers to the mask. And the structures all
+ have pointers to each other, or embed each other inside themselves,
+ and "pdev" sometimes means "platform device" and sometimes it means
+ "PCI device".
+
+ So to make it clear in the code when you actually use this new field,
+ give it a unique name (it really should be something even more unique
+ like "platform_device_dma_mask", since it's per platform device, not
+ per platform, but that gets old really fast, and this is unique
+ enough in context).
+
+ To further clarify when the field gets used, initialize it when we
+ actually start using it with the default value.
+
+ - Then, use this field instead of the random one-off allocation in
+ platform_device_register_full() that is now unnecessary since we now
+ already have a perfectly fine allocation for it in the platform
+ device structure.
+
+ - The above then allows us to fix the actual bug, where the error path
+ of platform_device_register_full() would unconditionally free the
+ platform device DMA allocation with 'kfree()'.
+
+ That kfree() was dont regardless of whether the allocation had been
+ done earlier with the (now removed) kmalloc, or whether
+ setup_pdev_dma_masks() had already been used and the dma_mask pointer
+ pointed to the mask that was part of the platform device.
+
+It seems most people never triggered the error path, or only triggered
+it from a call chain that set an explicit pdevinfo->dma_mask value (and
+thus caused the unnecessary allocation that was "cleaned up" in the
+error path) before calling platform_device_register_full().
+
+Robin Murphy points out that in Artem's case the wdat_wdt driver failed
+in platform_device_add(), and that was the one that had called
+platform_device_register_full() with pdevinfo.dma_mask = 0, and would
+have caused that kfree() of pdev.dma_mask corrupting the heap.
+
+A later unrelated kmalloc() then oopsed due to the heap corruption.
+
+Fixes: cdfee5623290 ("driver core: initialize a default DMA mask for platform device")
+Reported-bisected-and-tested-by: Artem S. Tashkinov <aros@gmx.com>
+Reviewed-by: Robin Murphy <robin.murphy@arm.com>
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+ drivers/base/platform.c | 25 ++++++-------------------
+ include/linux/platform_device.h | 2 +-
+ 2 files changed, 7 insertions(+), 20 deletions(-)
+
+diff --git a/drivers/base/platform.c b/drivers/base/platform.c
+index 7fa654f1288b..b5ce7b085795 100644
+--- a/drivers/base/platform.c
++++ b/drivers/base/platform.c
+@@ -363,10 +363,10 @@ static void setup_pdev_dma_masks(struct platform_device *pdev)
+ {
+ if (!pdev->dev.coherent_dma_mask)
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+- if (!pdev->dma_mask)
+- pdev->dma_mask = DMA_BIT_MASK(32);
+- if (!pdev->dev.dma_mask)
+- pdev->dev.dma_mask = &pdev->dma_mask;
++ if (!pdev->dev.dma_mask) {
++ pdev->platform_dma_mask = DMA_BIT_MASK(32);
++ pdev->dev.dma_mask = &pdev->platform_dma_mask;
++ }
+ };
+
+ /**
+@@ -662,20 +662,8 @@ struct platform_device *platform_device_register_full(
+ pdev->dev.of_node_reused = pdevinfo->of_node_reused;
+
+ if (pdevinfo->dma_mask) {
+- /*
+- * This memory isn't freed when the device is put,
+- * I don't have a nice idea for that though. Conceptually
+- * dma_mask in struct device should not be a pointer.
+- * See http://thread.gmane.org/gmane.linux.kernel.pci/9081
+- */
+- pdev->dev.dma_mask =
+- kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
+- if (!pdev->dev.dma_mask)
+- goto err;
+-
+- kmemleak_ignore(pdev->dev.dma_mask);
+-
+- *pdev->dev.dma_mask = pdevinfo->dma_mask;
++ pdev->platform_dma_mask = pdevinfo->dma_mask;
++ pdev->dev.dma_mask = &pdev->platform_dma_mask;
+ pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
+ }
+
+@@ -700,7 +688,6 @@ struct platform_device *platform_device_register_full(
+ if (ret) {
+ err:
+ ACPI_COMPANION_SET(&pdev->dev, NULL);
+- kfree(pdev->dev.dma_mask);
+ platform_device_put(pdev);
+ return ERR_PTR(ret);
+ }
+diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
+index 276a03c24691..041bfa412aa0 100644
+--- a/include/linux/platform_device.h
++++ b/include/linux/platform_device.h
+@@ -24,7 +24,7 @@ struct platform_device {
+ int id;
+ bool id_auto;
+ struct device dev;
+- u64 dma_mask;
++ u64 platform_dma_mask;
+ u32 num_resources;
+ struct resource *resource;
+
+--
+2.25.1
+