[PATCH 02/11] driver core: Set DMA ownership during driver bind/unbind

Lu Baolu
2021-11-15T02:10:51Z

This extends really_probe() to allow checking for dma ownership conflict
during the driver binding process. By default, the DMA_OWNER_KERNEL is
claimed for the bound driver before calling its .probe() callback. If this
operation fails (e.g. the iommu group of the target device already has the
DMA_OWNER_USER set), the binding process is aborted to avoid breaking the
security contract for devices in the iommu group.

Without this change, the vfio driver has to listen to a bus BOUND_DRIVER
event and then BUG_ON() in case of dma ownership conflict. This leads to
bad user experience since careless driver binding operation may crash the
system if the admin overlooks the group restriction.

Aside from bad design, this leads to a security problem as a root user,
even with lockdown=integrity, can force the kernel to BUG.

Driver may set a new flag (suppress_auto_claim_dma_owner) to disable auto
claim in the binding process. Examples include kernel drivers (pci_stub,
PCI bridge drivers, etc.) which don't trigger DMA at all thus can be safely
exempted in DMA ownership check and userspace framework drivers (vfio/vdpa
etc.) which need to manually claim DMA_OWNER_USER when assigning a device
to userspace.

Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/linux-iommu/20210922123931.GI327412@nvidia.com/
Link: https://lore.kernel.org/linux-iommu/20210928115751.GK964074@nvidia.com/
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
---
include/linux/device/driver.h | 7 ++++++-
drivers/base/dd.c | 12 ++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h
index a498ebcf4993..25d39c64c4d9 100644
--- a/include/linux/device/driver.h
+++ b/include/linux/device/driver.h
@@ -54,6 +54,10 @@ enum probe_type {
* @owner: The module owner.
* @mod_name: Used for built-in modules.
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
+ * @suppress_auto_claim_dma_owner: Disable auto claiming of kernel DMA owner.
+ * Drivers which don't require DMA or want to manually claim the
+ * owner type (e.g. userspace driver frameworks) could set this
+ * flag.
* @probe_type: Type of the probe (synchronous or asynchronous) to use.
* @of_match_table: The open firmware table.
* @acpi_match_table: The ACPI match table.
@@ -99,7 +103,8 @@ struct device_driver {
struct module *owner;
const char *mod_name; /* used for built-in modules */

- bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
+ bool suppress_bind_attrs:1; /* disables bind/unbind via sysfs */
+ bool suppress_auto_claim_dma_owner:1;
enum probe_type probe_type;

const struct of_device_id *of_match_table;
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 68ea1f949daa..ab3333351f19 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -28,6 +28,7 @@
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/slab.h>
+#include <linux/iommu.h>

#include "base.h"
#include "power/power.h"
@@ -566,6 +567,12 @@ static int really_probe(struct device *dev, struct device_driver *drv)
goto done;
}

+ if (!drv->suppress_auto_claim_dma_owner) {
+ ret = iommu_device_set_dma_owner(dev, DMA_OWNER_KERNEL, NULL);
+ if (ret)
+ return ret;
+ }
+
re_probe:
dev->driver = drv;

@@ -673,6 +680,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
dev->pm_domain->dismiss(dev);
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);
+ if (!drv->suppress_auto_claim_dma_owner)
+ iommu_device_release_dma_owner(dev, DMA_OWNER_KERNEL);
done:
return ret;
}
@@ -1215,6 +1224,9 @@ static void __device_release_driver(struct device *dev, struct device *parent)
pm_runtime_reinit(dev);
dev_pm_set_driver_flags(dev, 0);

+ if (!drv->suppress_auto_claim_dma_owner)
+ iommu_device_release_dma_owner(dev, DMA_OWNER_KERNEL);
+
klist_remove(&dev->p->knode_driver);
device_pm_check_callbacks(dev);
if (dev->bus)
--
2.25.1