* [PATCH 0/2] lockdown: Prevent booting with lockdown enabled but IOMMU disabled
@ 2026-11-02 14:00 Mario Limonciello (AMD)
2026-11-02 14:00 ` [PATCH 1/2] iommu: Add iommu_is_available() API Mario Limonciello (AMD)
2026-11-02 14:00 ` [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU Mario Limonciello (AMD)
0 siblings, 2 replies; 5+ messages in thread
From: Mario Limonciello (AMD) @ 2026-11-02 14:00 UTC (permalink / raw)
To: Joerg Roedel (AMD), Will Deacon, Robin Murphy, Nicolas Bouchinet,
Xiu Jianfeng, Paul Moore, James Morris, Serge E. Hallyn
Cc: iommu, linux-kernel, linux-security-module,
Mario Limonciello (AMD)
Lockdown is a kernel LSM that restricts access to sensitive kernel
resources (e.g. /dev/mem, kernel modules, perf, tracing, kprobes,
BPF access to kernel memory). However, without IOMMU, any PCI/PCIe
device can perform DMA to arbitrary physical memory, effectively
bypassing all lockdown protections.
A malicious or compromised device can:
- Read kernel memory directly (encryption keys, credentials, secrets)
- Write to kernel memory directly (code patches, rootkits, backdoors)
- Circumvent all lockdown LSM checks (which run inside the kernel)
This series adds a two-phase check to prevent booting with lockdown
enabled and IOMMU disabled:
1. Early check (in lockdown_lsm_init()): On x86, check the no_iommu
flag, which is set early by the "iommu=off" kernel cmdline option.
If lockdown is active and no_iommu is set, panic with a clear error.
2. Late check (late_initcall): On all architectures, check
iommu_is_available() which returns true if any IOMMU driver
registered successfully. This catches cases where:
- IOMMU hardware is absent on the platform
- IOMMU was disabled by driver initialization failure
- IOMMU was not detected (broken firmware, missing ACPI tables)
This follows the precedent set by s390 UV code which panics when
security cannot be guaranteed ("System security cannot be guaranteed
unless the system panics now").
This also adds a generic iommu_is_available() API in the IOMMU
subsystem, which is used by the lockdown check. Any IOMMU driver
that successfully registers will automatically call
iommu_set_available() from iommu_device_register().
Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
Mario Limonciello (AMD) (2):
iommu: Add iommu_is_available() API
security: lockdown: Prevent boot with lockdown but no IOMMU
drivers/iommu/iommu.c | 48 ++++++++++++++++++++++++++++++++
include/linux/iommu.h | 14 ++++++++++
security/lockdown/lockdown.c | 65 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 127 insertions(+)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261014-lockdown-iommu-08c763d9f91dd
Best regards,
--
Mario Limonciello <superm1@kernel.org>
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] iommu: Add iommu_is_available() API
2026-11-02 14:00 [PATCH 0/2] lockdown: Prevent booting with lockdown enabled but IOMMU disabled Mario Limonciello (AMD)
@ 2026-11-02 14:00 ` Mario Limonciello (AMD)
2026-11-02 14:11 ` sashiko-bot
2026-11-02 14:00 ` [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU Mario Limonciello (AMD)
1 sibling, 1 reply; 5+ messages in thread
From: Mario Limonciello (AMD) @ 2026-11-02 14:00 UTC (permalink / raw)
To: Joerg Roedel (AMD), Will Deacon, Robin Murphy, Nicolas Bouchinet,
Xiu Jianfeng, Paul Moore, James Morris, Serge E. Hallyn
Cc: iommu, linux-kernel, linux-security-module,
Mario Limonciello (AMD)
Add a generic API to query whether IOMMU subsystem is available on the
platform. This returns true if at least one IOMMU driver has been
registered and successfully probed, meaning hardware IOMMU support is
available on the platform.
The iommu_set_available() function is called from iommu_device_register()
when an IOMMU driver successfully registers, marking IOMMU as available.
This will be used by security subsystems (e.g. lockdown) that require
IOMMU isolation to provide meaningful security guarantees. Without IOMMU,
PCI/PCIe devices can perform DMA to arbitrary physical memory, bypassing
all software-level security protections.
Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
drivers/iommu/iommu.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iommu.h | 14 ++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b486b8bbd1fcd..d042944713ab4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -309,6 +309,13 @@ int iommu_device_register(struct iommu_device *iommu,
iommu_device_unregister(iommu);
else
WRITE_ONCE(iommu->ready, true);
+ /*
+ * Mark IOMMU as available if registration succeeded and bus probing
+ * succeeded. This enables security checks (e.g. lockdown IOMMU check)
+ * that depend on knowing whether hardware IOMMU isolation is present.
+ */
+ if (!err)
+ iommu_set_available();
return err;
}
EXPORT_SYMBOL_GPL(iommu_device_register);
@@ -2961,6 +2968,47 @@ static int __init iommu_init(void)
}
core_initcall(iommu_init);
+static bool iommu_available __read_mostly;
+
+/**
+ * iommu_is_available() - Check if IOMMU subsystem is available
+ *
+ * Returns true if at least one IOMMU driver has been registered and
+ * successfully probed, meaning hardware IOMMU support is available on
+ * the platform. Returns false if no IOMMU driver is available.
+ *
+ * This function is intended for use by security subsystems that require
+ * IOMMU isolation to provide meaningful security guarantees (e.g. lockdown).
+ *
+ * Note: This function only reports on whether IOMMU drivers are present,
+ * not whether specific devices are covered. Use device_iommu_capable() or
+ * iommu_group_get() to check per-device IOMMU coverage.
+ *
+ * Context: Can be called at any time after module initialization has begun.
+ * Returns a snapshot of the IOMMU availability state.
+ *
+ * Return: True if IOMMU is available, false otherwise.
+ */
+bool iommu_is_available(void)
+{
+ return iommu_available;
+}
+EXPORT_SYMBOL_GPL(iommu_is_available);
+
+/**
+ * iommu_set_available() - Mark IOMMU as available
+ *
+ * Called by IOMMU drivers during probe to indicate that hardware IOMMU
+ * support is functional on this platform.
+ *
+ * Context: Called from driver probe paths.
+ */
+void iommu_set_available(void)
+{
+ iommu_available = true;
+}
+EXPORT_SYMBOL_GPL(iommu_set_available);
+
int iommu_set_pgtable_quirks(struct iommu_domain *domain,
unsigned long quirk)
{
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ef5043752a365..d77237fc880ae 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1222,6 +1222,13 @@ void iommu_detach_device_pasid(struct iommu_domain *domain,
ioasid_t iommu_alloc_global_pasid(struct device *dev);
void iommu_free_global_pasid(ioasid_t pasid);
+/*
+ * IOMMU availability API - for use by subsystems that require IOMMU
+ * isolation to provide security guarantees.
+ */
+bool iommu_is_available(void);
+void iommu_set_available(void);
+
/* PCI device reset functions */
int pci_dev_reset_iommu_prepare(struct pci_dev *pdev);
void pci_dev_reset_iommu_done(struct pci_dev *pdev);
@@ -1557,6 +1564,13 @@ static inline int pci_dev_reset_iommu_prepare(struct pci_dev *pdev)
static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
{
}
+
+static inline bool iommu_is_available(void)
+{
+ return false;
+}
+static inline void iommu_set_available(void) {}
+
#endif /* CONFIG_IOMMU_API */
#ifdef CONFIG_IRQ_MSI_IOMMU
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU
2026-11-02 14:00 [PATCH 0/2] lockdown: Prevent booting with lockdown enabled but IOMMU disabled Mario Limonciello (AMD)
2026-11-02 14:00 ` [PATCH 1/2] iommu: Add iommu_is_available() API Mario Limonciello (AMD)
@ 2026-11-02 14:00 ` Mario Limonciello (AMD)
2026-11-02 14:28 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Mario Limonciello (AMD) @ 2026-11-02 14:00 UTC (permalink / raw)
To: Joerg Roedel (AMD), Will Deacon, Robin Murphy, Nicolas Bouchinet,
Xiu Jianfeng, Paul Moore, James Morris, Serge E. Hallyn
Cc: iommu, linux-kernel, linux-security-module,
Mario Limonciello (AMD)
Lockdown is a kernel LSM that restricts access to sensitive kernel
resources (e.g. /dev/mem, kernel modules, perf, tracing, kprobes,
BPF access to kernel memory). However, without IOMMU, any PCI/PCIe
device can perform DMA to arbitrary physical memory, effectively
bypassing all lockdown protections.
A malicious or compromised device can:
- Read kernel memory directly (encryption keys, credentials, secrets)
- Write to kernel memory directly (code patches, rootkits, backdoors)
- Circumvent all lockdown LSM checks (which run inside the kernel)
Add a two-phase check to prevent booting with lockdown enabled and
IOMMU disabled:
1. Early check (in lockdown_lsm_init()): On x86, check the no_iommu
flag, which is set early by the 'iommu=off' kernel cmdline option.
If lockdown is active and no_iommu is set, panic with a clear error.
2. Late check (late_initcall): On all architectures, check
iommu_is_available() which returns true if any IOMMU driver
registered successfully. This catches cases where:
- IOMMU hardware is absent on the platform
- IOMMU was disabled by driver initialization failure
- IOMMU was not detected (broken firmware, missing ACPI tables)
This follows the precedent set by s390 UV code which panics when
security cannot be guaranteed ("System security cannot be guaranteed
unless the system panics now").
Also update iommu_device_register() to call iommu_set_available() on
successful registration, so all IOMMU drivers automatically signal
their availability.
Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
---
security/lockdown/lockdown.c | 65 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index 8d46886d2cca0..6ce1490188d5d 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -13,8 +13,14 @@
#include <linux/security.h>
#include <linux/export.h>
#include <linux/lsm_hooks.h>
+#include <linux/init.h>
#include <uapi/linux/lsm.h>
+#if IS_ENABLED(CONFIG_X86)
+#include <asm/iommu.h>
+#endif
+#include <linux/iommu.h>
+
static enum lockdown_reason kernel_locked_down;
static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
@@ -88,6 +94,25 @@ static int __init lockdown_lsm_init(void)
#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
#endif
+
+ /*
+ * If lockdown was activated (via command line or kernel config)
+ * and IOMMU is disabled, refuse to boot. Without IOMMU, any
+ * PCI/PCIe device can perform DMA to arbitrary physical memory,
+ * completely bypassing lockdown protections.
+ *
+ * On x86, 'no_iommu' is set early by the 'iommu=off' cmdline
+ * option during parse_early_param(), before this init function.
+ */
+#if IS_ENABLED(CONFIG_X86)
+ if (kernel_locked_down > LOCKDOWN_NONE && no_iommu) {
+ pr_err("FATAL: Lockdown is active but IOMMU is disabled.\n");
+ pr_err("FATAL: PCI devices can perform DMA to arbitrary memory,\n");
+ pr_err("FATAL: bypassing all lockdown protections.\n");
+ panic("Lockdown enabled but IOMMU is disabled (no_iommu)\n");
+ }
+#endif
+
security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
&lockdown_lsmid);
return 0;
@@ -170,3 +195,43 @@ DEFINE_LSM(lockdown) = {
.init = lockdown_lsm_init,
.initcall_core = lockdown_secfs_init,
};
+
+/*
+ * Late init call to verify IOMMU availability when lockdown is active.
+ * This catches cases where:
+ * - IOMMU hardware is absent on the platform
+ * - IOMMU was disabled by driver initialization failure
+ * - IOMMU was not detected (e.g. broken firmware, missing ACPI tables)
+ *
+ * On x86, we also check no_iommu here as a double-check. The early check
+ * in lockdown_lsm_init() catches 'iommu=off' from the cmdline before
+ * lockdown_lsm_init() runs, but this late check covers any missed cases.
+ */
+static int __init lockdown_check_iommu(void)
+{
+ if (kernel_locked_down > LOCKDOWN_NONE) {
+ /*
+ * Check generic IOMMU availability (all architectures).
+ * iommu_is_available() returns true if any IOMMU driver
+ * registered successfully.
+ */
+ if (!iommu_is_available()) {
+ pr_err("FATAL: Lockdown is active but no IOMMU driver\n");
+ pr_err("FATAL: registered. PCI devices can DMA to\n");
+ pr_err("FATAL: arbitrary memory, bypassing lockdown.\n");
+ panic("Lockdown enabled but IOMMU not available\n");
+ }
+#if IS_ENABLED(CONFIG_X86)
+ /*
+ * Also double-check x86-specific flag.
+ */
+ if (no_iommu) {
+ pr_err("FATAL: Lockdown is active but IOMMU disabled\n");
+ pr_err("FATAL: via kernel command line (iommu=off).\n");
+ panic("Lockdown enabled but IOMMU disabled (iommu=off)\n");
+ }
+#endif
+ }
+ return 0;
+}
+late_initcall(lockdown_check_iommu);
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] iommu: Add iommu_is_available() API
2026-11-02 14:00 ` [PATCH 1/2] iommu: Add iommu_is_available() API Mario Limonciello (AMD)
@ 2026-11-02 14:11 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-02 14:11 UTC (permalink / raw)
Cc: linux-security-module
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] iommu: iommu_available never cleared in iommu_device_unregister()
- [Medium] iommu: iommu_device_register() marks non-translating IOMMUs available
- [Low] iommu: unnecessary export and wrong doc for iommu_set_available()
- [Low] iommu: unmarked lockless access to iommu_available
--
commit 994e0def279fa954a8ab08056b43fb15157b3edd
Author: Mario Limonciello (AMD) <superm1@kernel.org>
iommu: Add iommu_is_available() API
This commit introduces iommu_is_available() and iommu_set_available() so
that security subsystems such as lockdown can query whether an IOMMU driver
has registered and probed on the platform.
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index b486b8bbd1fcd..d042944713ab4 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -309,6 +309,13 @@ int iommu_device_register(struct iommu_device *iommu,
> iommu_device_unregister(iommu);
> else
> WRITE_ONCE(iommu->ready, true);
> + /*
> + * Mark IOMMU as available if registration succeeded and bus probing
> + * succeeded. This enables security checks (e.g. lockdown IOMMU check)
> + * that depend on knowing whether hardware IOMMU isolation is present.
> + */
> + if (!err)
> + iommu_set_available();
[Severity: High]
What happens if all registered IOMMU devices are later unregistered via
iommu_device_unregister()?
Several IOMMU drivers perform additional fallible initialization steps after
calling iommu_device_register() and unwind with iommu_device_unregister() on
failure. For example, in drivers/iommu/amd/init.c:
iommu_init_pci()
iommu_device_register()
pci_enable_device()
and later in state_next():
state_next()
amd_iommu_enable_interrupts()
free_iommu_resources()
free_iommu_all()
free_iommu_one()
free_sysfs()
iommu_device_unregister(&iommu->iommu);
Similar post-registration error paths exist in mtk_iommu_probe() (when
component_master_add_with_match() fails) and sprd_iommu_probe() (when
sprd_iommu_clk_enable() or sprd_iommu_get_version() fails), as well as
driver unbind and hot-unplug paths.
Because iommu_device_unregister() removes the instance from
iommu_device_list without clearing or recalculating iommu_available when the
list becomes empty, won't iommu_is_available() permanently return true even
when no IOMMU device remains registered?
[Severity: Medium]
Could setting iommu_available unconditionally when iommu_device_register()
succeeds report true on platforms where no DMA translation or isolation is
actually active?
In drivers/iommu/iommu.c, probe_iommu_group() converts -ENODEV from
__iommu_probe_device() into 0, so bus_iommu_probe() succeeds even when an
IOMMU driver rejects all devices or provides no DMA translation:
1. In drivers/iommu/amd/init.c, iommu_init_pci() calls
iommu_device_register() even when amd_iommu_pgtable == PD_MODE_NONE
(where DMA translation is unsupported and only interrupt remapping is
used, causing amd_iommu_probe_device() to return ERR_PTR(-ENODEV) for
every device):
ret = iommu_device_register(&iommu->iommu, &amd_iommu_ops, NULL);
if (ret || amd_iommu_pgtable == PD_MODE_NONE) {
iommu_device_sysfs_remove(&iommu->iommu);
}
2. In drivers/xen/grant-dma-iommu.c, grant_dma_iommu_probe() registers
grant_dma_iommu_ops, whose probe_device callback unconditionally returns
ERR_PTR(-ENODEV) without providing hardware DMA isolation.
3. When default passthrough mode is active (iommu_default_passthrough() is
true via iommu=pt, iommu.passthrough=1, or
CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y), internal PCI/PCIe devices are placed
in IOMMU_DOMAIN_IDENTITY domains with unrestricted DMA access to physical
memory.
Should iommu_is_available() account for whether active DMA translation is
present rather than only checking if iommu_device_register() was called?
> return err;
> }
> EXPORT_SYMBOL_GPL(iommu_device_register);
> @@ -2961,6 +2968,47 @@ static int __init iommu_init(void)
> }
> core_initcall(iommu_init);
>
> +static bool iommu_available __read_mostly;
> +
> +/**
> + * iommu_is_available() - Check if IOMMU subsystem is available
[ ... ]
> + */
> +bool iommu_is_available(void)
> +{
> + return iommu_available;
> +}
> +EXPORT_SYMBOL_GPL(iommu_is_available);
> +
> +/**
> + * iommu_set_available() - Mark IOMMU as available
> + *
> + * Called by IOMMU drivers during probe to indicate that hardware IOMMU
> + * support is functional on this platform.
> + *
> + * Context: Called from driver probe paths.
> + */
> +void iommu_set_available(void)
> +{
> + iommu_available = true;
> +}
> +EXPORT_SYMBOL_GPL(iommu_set_available);
[Severity: Low]
Since iommu_is_available() can be called at any time after module
initialization has begun while deferred or hotplug IOMMU driver probes may
call iommu_device_register() concurrently without holding a shared lock,
should the accesses to iommu_available use READ_ONCE() and WRITE_ONCE()
(similar to WRITE_ONCE(iommu->ready, true) in iommu_device_register()), or
check !list_empty(&iommu_device_list) under iommu_device_lock, to avoid
KCSAN data-race warnings?
[Severity: Low]
Is there any reason to declare iommu_set_available() in
include/linux/iommu.h and export it via EXPORT_SYMBOL_GPL()?
The kernel-doc comment states that it is called by IOMMU drivers during
probe, but its only caller is iommu_device_register() in
drivers/iommu/iommu.c. Could iommu_set_available() be made static (or folded
directly into iommu_device_register()) so arbitrary modules cannot flip this
security-sensitive state without registering an IOMMU device?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102-lockdown-iommu-v1-0-00f3c2be4c9f@kernel.org?part=1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU
2026-11-02 14:00 ` [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU Mario Limonciello (AMD)
@ 2026-11-02 14:28 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-02 14:28 UTC (permalink / raw)
Cc: linux-security-module
Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] security: lockdown: unconditional boot panic in lockdown_check_iommu()
- [Medium] security: lockdown: runtime activation in lockdown_write() skips IOMMU check
- [Medium] iommu: iommu_available stays true after iommu_device_unregister() on failure
- [Medium] security: lockdown: passthrough mode bypasses lockdown_check_iommu()
- [Medium] security: lockdown: raw late_initcall() runs when lockdown LSM is disabled
- [Medium] security: lockdown: premature no_iommu panic in lockdown_lsm_init()
- [Low] security: lockdown: unreachable no_iommu check in lockdown_check_iommu()
- [Low] security: lockdown: commit message claims change to iommu_device_register()
--
commit 437687711e0f75d4ef1930e13f9b1f994ec32b30
Author: Mario Limonciello (AMD) <superm1@kernel.org>
security: lockdown: Prevent boot with lockdown but no IOMMU
This commit adds an early check on x86 and a generic late_initcall check to
panic during boot if kernel lockdown is enabled while an IOMMU is disabled
or unavailable.
> security: lockdown: Prevent boot with lockdown but no IOMMU
>
> Lockdown is a kernel LSM that restricts access to sensitive kernel
> resources (e.g. /dev/mem, kernel modules, perf, tracing, kprobes,
> BPF access to kernel memory). However, without IOMMU, any PCI/PCIe
> device can perform DMA to arbitrary physical memory, effectively
> bypassing all lockdown protections.
>
> A malicious or compromised device can:
> - Read kernel memory directly (encryption keys, credentials, secrets)
> - Write to kernel memory directly (code patches, rootkits, backdoors)
> - Circumvent all lockdown LSM checks (which run inside the kernel)
>
> Add a two-phase check to prevent booting with lockdown enabled and
> IOMMU disabled:
>
> 1. Early check (in lockdown_lsm_init()): On x86, check the no_iommu
> flag, which is set early by the 'iommu=off' kernel cmdline option.
> If lockdown is active and no_iommu is set, panic with a clear error.
>
> 2. Late check (late_initcall): On all architectures, check
> iommu_is_available() which returns true if any IOMMU driver
> registered successfully. This catches cases where:
> - IOMMU hardware is absent on the platform
> - IOMMU was disabled by driver initialization failure
> - IOMMU was not detected (broken firmware, missing ACPI tables)
>
> This follows the precedent set by s390 UV code which panics when
> security cannot be guaranteed ("System security cannot be guaranteed
> unless the system panics now").
>
> Also update iommu_device_register() to call iommu_set_available() on
> successful registration, so all IOMMU drivers automatically signal
> their availability.
>
> Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
[Severity: Low]
This isn't a bug, but this commit only touches security/lockdown/lockdown.c,
while the change to iommu_device_register() in drivers/iommu/iommu.c was
made in the preceding commit ("iommu: Add iommu_is_available() API"). Should
the last paragraph be removed from this commit message?
> diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
> index 8d46886d2cca0..6ce1490188d5d 100644
> --- a/security/lockdown/lockdown.c
> +++ b/security/lockdown/lockdown.c
[ ... ]
> @@ -88,6 +94,25 @@ static int __init lockdown_lsm_init(void)
> #elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
> lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
> #endif
> +
> + /*
> + * If lockdown was activated (via command line or kernel config)
> + * and IOMMU is disabled, refuse to boot. Without IOMMU, any
> + * PCI/PCIe device can perform DMA to arbitrary physical memory,
> + * completely bypassing lockdown protections.
> + *
> + * On x86, 'no_iommu' is set early by the 'iommu=off' cmdline
> + * option during parse_early_param(), before this init function.
> + */
> +#if IS_ENABLED(CONFIG_X86)
> + if (kernel_locked_down > LOCKDOWN_NONE && no_iommu) {
> + pr_err("FATAL: Lockdown is active but IOMMU is disabled.\n");
> + pr_err("FATAL: PCI devices can perform DMA to arbitrary memory,\n");
> + pr_err("FATAL: bypassing all lockdown protections.\n");
> + panic("Lockdown enabled but IOMMU is disabled (no_iommu)\n");
> + }
> +#endif
[Severity: Medium]
Can checking no_iommu in lockdown_lsm_init() cause a premature boot panic on
Intel TXT (tboot) systems?
During start_kernel(), parse_early_param() runs iommu_setup() in
arch/x86/kernel/pci-dma.c, which sets no_iommu = 1 if iommu=off was passed on
the command line. Then early_security_init() or security_init() calls
lockdown_lsm_init() before do_initcalls() executes.
However, on Intel TXT launches, rootfs_initcall(pci_iommu_init) later calls
intel_iommu_init() -> tboot_force_iommu() in drivers/iommu/intel/iommu.c,
which forces DMA remapping on and clears no_iommu:
static __init void tboot_force_iommu(void)
{
if (!tboot_enabled() || intel_iommu_tboot_noforce)
return;
...
dmar_policy = DMAR_FORCE_ON;
no_iommu = 0;
}
Because lockdown_lsm_init() runs before rootfs_initcall(pci_iommu_init), will
it panic before tboot_force_iommu() has a chance to clear no_iommu = 0?
> +
> security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
> &lockdown_lsmid);
> return 0;
> @@ -170,3 +195,43 @@ DEFINE_LSM(lockdown) = {
> .init = lockdown_lsm_init,
> .initcall_core = lockdown_secfs_init,
> };
> +
> +/*
> + * Late init call to verify IOMMU availability when lockdown is active.
> + * This catches cases where:
> + * - IOMMU hardware is absent on the platform
> + * - IOMMU was disabled by driver initialization failure
> + * - IOMMU was not detected (e.g. broken firmware, missing ACPI tables)
> + *
> + * On x86, we also check no_iommu here as a double-check. The early check
> + * in lockdown_lsm_init() catches 'iommu=off' from the cmdline before
> + * lockdown_lsm_init() runs, but this late check covers any missed cases.
> + */
> +static int __init lockdown_check_iommu(void)
> +{
> + if (kernel_locked_down > LOCKDOWN_NONE) {
[Severity: Medium]
What happens when lockdown is enabled at runtime via securityfs rather than
at boot?
When the kernel is built with CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y and booted
without a lockdown= parameter, kernel_locked_down is LOCKDOWN_NONE when both
lockdown_lsm_init() and lockdown_check_iommu() run.
If userspace subsequently enables lockdown by writing to
/sys/kernel/security/lockdown, lockdown_write() in
security/lockdown/lockdown.c calls lock_kernel_down("securityfs", level):
static ssize_t lockdown_write(struct file *file, const char __user *buf,
size_t n, loff_t *ppos)
{
...
if (label && !strcmp(state, label))
err = lock_kernel_down("securityfs", level);
...
}
Since lock_kernel_down() updates kernel_locked_down without checking
iommu_is_available() or no_iommu, doesn't runtime activation bypass the IOMMU
requirement?
> + /*
> + * Check generic IOMMU availability (all architectures).
> + * iommu_is_available() returns true if any IOMMU driver
> + * registered successfully.
> + */
> + if (!iommu_is_available()) {
> + pr_err("FATAL: Lockdown is active but no IOMMU driver\n");
> + pr_err("FATAL: registered. PCI devices can DMA to\n");
> + pr_err("FATAL: arbitrary memory, bypassing lockdown.\n");
> + panic("Lockdown enabled but IOMMU not available\n");
> + }
[Severity: High]
Won't panicking unconditionally when !iommu_is_available() break boot across
several valid configurations, architectures, and driver models?
1. Kernels with CONFIG_IOMMU_API=n (or CONFIG_IOMMU_SUPPORT=n):
CONFIG_SECURITY_LOCKDOWN_LSM in security/lockdown/Kconfig does not depend on
CONFIG_IOMMU_API, and include/linux/iommu.h defines the fallback stub as:
static inline bool iommu_is_available(void)
{
return false;
}
For example, arch/powerpc/configs/skiroot_defconfig disables
CONFIG_IOMMU_SUPPORT while enabling CONFIG_SECURITY_LOCKDOWN_LSM=y and
CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY=y, so it will now panic on every
boot. On PowerPC more generally, arch/powerpc/kernel/iommu.c enforces
hardware TCE DMA translation even when CONFIG_SPAPR_TCE_IOMMU (which selects
CONFIG_IOMMU_API and registers IOMMU devices in
spapr_tce_setup_phb_iommus_initcall() only when hose_list is non-empty) is
disabled.
2. Architectures that only call iommu_device_register() per attached PCI
device:
On s390, zpci_init_iommu() in drivers/iommu/s390-iommu.c is only called when
a zPCI device is discovered via zpci_add_device():
int zpci_init_iommu(struct zpci_dev *zdev)
{
...
if (zdev->rtr_avail) {
rc = iommu_device_register(&zdev->iommu_dev,
&s390_iommu_rtr_ops, NULL);
} else {
rc = iommu_device_register(&zdev->iommu_dev, &s390_iommu_ops,
NULL);
}
...
}
On any s390 LPAR or guest without attached zPCI devices (such as systems
using only CCW or virtio-ccw I/O, or with CONFIG_PCI=n),
iommu_device_register() is never called, causing lockdown_check_iommu() to
panic claiming PCI devices can DMA to arbitrary memory when no PCI devices
are present.
3. Modular and deferred-probe IOMMU drivers:
Several IOMMU drivers in drivers/iommu/Kconfig are tristate (such as
CONFIG_ARM_SMMU, CONFIG_ARM_SMMU_V3, CONFIG_VIRTIO_IOMMU, CONFIG_APPLE_DART,
and CONFIG_MTK_IOMMU). If built as modules (=m), they cannot be loaded from
initramfs before late_initcall(lockdown_check_iommu) runs. In addition,
because security/ is linked before drivers/ in the top-level Kbuild,
late_initcall(lockdown_check_iommu) in security/lockdown/lockdown.c runs
before late_initcall(deferred_probe_initcall) in drivers/base/dd.c, so any
built-in IOMMU driver whose probe returned -EPROBE_DEFER will not have been
re-probed yet when lockdown_check_iommu() panics.
4. Virtual machines and non-PCI systems:
Guests (such as KVM/QEMU, Xen, or cloud instances) and non-PCI platforms
booting with UEFI Secure Boot or lockdown=integrity without an emulated
vIOMMU will also unconditionally panic at boot.
[Severity: Medium]
Can iommu_is_available() still return true after an IOMMU driver fails
initialization following iommu_device_register()?
In drivers/iommu/iommu.c, iommu_device_register() sets iommu_available = true
via iommu_set_available(), but iommu_device_unregister() removes the device
from iommu_device_list without clearing or updating iommu_available when the
list becomes empty:
void iommu_device_unregister(struct iommu_device *iommu)
{
for (int i = 0; i < ARRAY_SIZE(iommu_buses); i++)
bus_for_each_dev(iommu_buses[i], NULL, iommu, remove_iommu_group);
spin_lock(&iommu_device_lock);
list_del(&iommu->list);
spin_unlock(&iommu_device_lock);
}
Several IOMMU drivers register the iommu_device before initialization is
complete and call iommu_device_unregister() in their error unwind paths:
- In drivers/iommu/amd/init.c, iommu_init_pci() calls
iommu_device_register() before pci_enable_device() and before
amd_iommu_enable_interrupts() in state_next(). If either fails, state_next()
calls disable_iommus() and free_iommu_resources() -> free_iommu_all() ->
free_sysfs() -> iommu_device_unregister(), leaving iommu_available set to
true. Additionally, iommu_init_pci() still calls iommu_device_register() when
amd_iommu_pgtable == PD_MODE_NONE (DMA translation unsupported).
- Platform IOMMU drivers such as mtk_iommu_probe() in
drivers/iommu/mtk_iommu.c, mtk_iommu_v1_probe(), qcom_iommu_device_probe(),
sprd_iommu_probe(), and sun50i_iommu_probe() also call
iommu_device_register() and roll back via iommu_device_unregister() if a
subsequent probe step fails.
Should iommu_is_available() check whether any active IOMMU devices remain
registered (or update the state in iommu_device_unregister()) rather than
using a one-way flag?
[Severity: Medium]
Does lockdown_check_iommu() also need to check iommu_default_passthrough()?
When booting with iommu=pt (handled in iommu_setup() in
arch/x86/kernel/pci-dma.c), iommu.passthrough=1 (in drivers/iommu/iommu.c),
or CONFIG_IOMMU_DEFAULT_PASSTHROUGH=y, no_iommu remains 0 and the IOMMU
driver still registers via iommu_device_register(), so iommu_is_available()
returns true. However, default domains use IOMMU_DOMAIN_IDENTITY
(iommu_default_passthrough() returns true), leaving PCI devices with
unrestricted identity-mapped DMA access to physical memory.
> +#if IS_ENABLED(CONFIG_X86)
> + /*
> + * Also double-check x86-specific flag.
> + */
> + if (no_iommu) {
> + pr_err("FATAL: Lockdown is active but IOMMU disabled\n");
> + pr_err("FATAL: via kernel command line (iommu=off).\n");
> + panic("Lockdown enabled but IOMMU disabled (iommu=off)\n");
> + }
> +#endif
[Severity: Low]
Is this no_iommu check in lockdown_check_iommu() reachable?
On x86, when no_iommu is non-zero, no IOMMU driver registers an IOMMU device,
so iommu_is_available() is false and the preceding !iommu_is_available()
check will always panic before reaching if (no_iommu).
> + }
> + return 0;
> +}
> +late_initcall(lockdown_check_iommu);
[Severity: Medium]
Should lockdown_check_iommu() be wired up via .initcall_late in
DEFINE_LSM(lockdown) instead of a raw late_initcall()?
In security/lockdown/lockdown.c, early_param("lockdown", lockdown_param) sets
kernel_locked_down during parse_early_param(), before security_init()
determines which LSMs are enabled:
static int __init lockdown_param(char *level)
{
...
if (strcmp(level, "integrity") == 0)
lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
else if (strcmp(level, "confidentiality") == 0)
lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
...
}
early_param("lockdown", lockdown_param);
If CONFIG_SECURITY_LOCKDOWN_LSM=y and CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=n,
and the lockdown LSM is disabled via CONFIG_LSM or the lsm= command-line
parameter while lockdown=integrity is passed on the command line,
lockdown_lsm_init() and .initcall_core = lockdown_secfs_init are skipped, but
a raw late_initcall(lockdown_check_iommu) will still run and panic. Using
.initcall_late in DEFINE_LSM(lockdown) would ensure it only runs when the
lockdown LSM is actually enabled.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102-lockdown-iommu-v1-0-00f3c2be4c9f@kernel.org?part=2
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-02 14:28 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-02 14:00 [PATCH 0/2] lockdown: Prevent booting with lockdown enabled but IOMMU disabled Mario Limonciello (AMD)
2026-11-02 14:00 ` [PATCH 1/2] iommu: Add iommu_is_available() API Mario Limonciello (AMD)
2026-11-02 14:11 ` sashiko-bot
2026-11-02 14:00 ` [PATCH 2/2] security: lockdown: Prevent boot with lockdown but no IOMMU Mario Limonciello (AMD)
2026-11-02 14:28 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox