iommu/vt-d: Fix NULL pointer dereference in dev_iommu_priv_set()
The dev_iommu_priv_set() must be called after probe_device(). This fixes
a NULL pointer deference bug when booting a system with kernel cmdline
"intel_iommu=on,igfx_off", where the dev_iommu_priv_set() is abused.
The following stacktrace was produced:
Command line: BOOT_IMAGE=/isolinux/bzImage console=tty1 intel_iommu=on,igfx_off
...
DMAR: Host address width 39
DMAR: DRHD base: 0x000000fed90000 flags: 0x0
DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap 1c0000c40660462 ecap 19e2ff0505e
DMAR: DRHD base: 0x000000fed91000 flags: 0x1
DMAR: dmar1: reg_base_addr fed91000 ver 1:0 cap d2008c40660462 ecap f050da
DMAR: RMRR base: 0x0000009aa9f000 end: 0x0000009aabefff
DMAR: RMRR base: 0x0000009d000000 end: 0x0000009f7fffff
DMAR: No ATSR found
BUG: kernel NULL pointer dereference, address: 0000000000000038
#PF: supervisor write access in kernel mode
#PF: error_code(0x0002) - not-present page
PGD 0 P4D 0
Oops: 0002 [#1] SMP PTI
CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.9.0-devel+ #2
Hardware name: LENOVO 20HGS0TW00/20HGS0TW00, BIOS N1WET46S (1.25s ) 03/30/2018
RIP: 0010:intel_iommu_init+0xed0/0x1136
Code: fe e9 61 02 00 00 bb f4 ff ff ff e9 57 02 00 00 48 63 d1 48 c1 e2 04 48
03 50 20 48 8b 12 48 85 d2 74 0b 48 8b 92 d0 02 00 00 48 89 7a 38 ff c1
e9 15 f5 ff ff 48 c7 c7 60 99 ac a7 49 c7 c7 a0
RSP: 0000:ffff96d180073dd0 EFLAGS: 00010282
RAX: ffff8c91037a7d20 RBX: 0000000000000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffffffffffffffff
RBP: ffff96d180073e90 R08: 0000000000000001 R09: ffff8c91039fe3c0
R10: 0000000000000226 R11: 0000000000000226 R12: 000000000000000b
R13: ffff8c910367c650 R14: ffffffffa8426d60 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff8c9107480000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000038 CR3: 00000004b100a001 CR4: 00000000003706e0
Call Trace:
? _raw_spin_unlock_irqrestore+0x1f/0x30
? call_rcu+0x10e/0x320
? trace_hardirqs_on+0x2c/0xd0
? rdinit_setup+0x2c/0x2c
? e820__memblock_setup+0x8b/0x8b
pci_iommu_init+0x16/0x3f
do_one_initcall+0x46/0x1e4
kernel_init_freeable+0x169/0x1b2
? rest_init+0x9f/0x9f
kernel_init+0xa/0x101
ret_from_fork+0x22/0x30
Modules linked in:
CR2: 0000000000000038
---[ end trace 3653722a6f936f18 ]---
Fixes: 01b9d4e211
("iommu/vt-d: Use dev_iommu_priv_get/set()")
Reported-by: Torsten Hilbrich <torsten.hilbrich@secunet.com>
Reported-by: Wendy Wang <wendy.wang@intel.com>
Signed-off-by: Lu Baolu <baolu.lu@linux.intel.com>
Tested-by: Torsten Hilbrich <torsten.hilbrich@secunet.com>
Link: https://lore.kernel.org/linux-iommu/96717683-70be-7388-3d2f-61131070a96a@secunet.com/
Link: https://lore.kernel.org/r/20200903065132.16879-1-baolu.lu@linux.intel.com
Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
parent
6e4e9ec650
commit
2d33b7d631
|
@ -364,7 +364,6 @@ static int iommu_skip_te_disable;
|
|||
int intel_iommu_gfx_mapped;
|
||||
EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
|
||||
|
||||
#define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1))
|
||||
#define DEFER_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-2))
|
||||
struct device_domain_info *get_domain_info(struct device *dev)
|
||||
{
|
||||
|
@ -374,8 +373,7 @@ struct device_domain_info *get_domain_info(struct device *dev)
|
|||
return NULL;
|
||||
|
||||
info = dev_iommu_priv_get(dev);
|
||||
if (unlikely(info == DUMMY_DEVICE_DOMAIN_INFO ||
|
||||
info == DEFER_DEVICE_DOMAIN_INFO))
|
||||
if (unlikely(info == DEFER_DEVICE_DOMAIN_INFO))
|
||||
return NULL;
|
||||
|
||||
return info;
|
||||
|
@ -742,11 +740,6 @@ struct context_entry *iommu_context_addr(struct intel_iommu *iommu, u8 bus,
|
|||
return &context[devfn];
|
||||
}
|
||||
|
||||
static int iommu_dummy(struct device *dev)
|
||||
{
|
||||
return dev_iommu_priv_get(dev) == DUMMY_DEVICE_DOMAIN_INFO;
|
||||
}
|
||||
|
||||
static bool attach_deferred(struct device *dev)
|
||||
{
|
||||
return dev_iommu_priv_get(dev) == DEFER_DEVICE_DOMAIN_INFO;
|
||||
|
@ -779,6 +772,53 @@ is_downstream_to_pci_bridge(struct device *dev, struct device *bridge)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool quirk_ioat_snb_local_iommu(struct pci_dev *pdev)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd;
|
||||
u32 vtbar;
|
||||
int rc;
|
||||
|
||||
/* We know that this device on this chipset has its own IOMMU.
|
||||
* If we find it under a different IOMMU, then the BIOS is lying
|
||||
* to us. Hope that the IOMMU for this device is actually
|
||||
* disabled, and it needs no translation...
|
||||
*/
|
||||
rc = pci_bus_read_config_dword(pdev->bus, PCI_DEVFN(0, 0), 0xb0, &vtbar);
|
||||
if (rc) {
|
||||
/* "can't" happen */
|
||||
dev_info(&pdev->dev, "failed to run vt-d quirk\n");
|
||||
return false;
|
||||
}
|
||||
vtbar &= 0xffff0000;
|
||||
|
||||
/* we know that the this iommu should be at offset 0xa000 from vtbar */
|
||||
drhd = dmar_find_matched_drhd_unit(pdev);
|
||||
if (!drhd || drhd->reg_base_addr - vtbar != 0xa000) {
|
||||
pr_warn_once(FW_BUG "BIOS assigned incorrect VT-d unit for Intel(R) QuickData Technology device\n");
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool iommu_is_dummy(struct intel_iommu *iommu, struct device *dev)
|
||||
{
|
||||
if (!iommu || iommu->drhd->ignored)
|
||||
return true;
|
||||
|
||||
if (dev_is_pci(dev)) {
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_IOAT_SNB &&
|
||||
quirk_ioat_snb_local_iommu(pdev))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd = NULL;
|
||||
|
@ -788,7 +828,7 @@ struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
|||
u16 segment = 0;
|
||||
int i;
|
||||
|
||||
if (!dev || iommu_dummy(dev))
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
if (dev_is_pci(dev)) {
|
||||
|
@ -805,7 +845,7 @@ struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
|||
dev = &ACPI_COMPANION(dev)->dev;
|
||||
|
||||
rcu_read_lock();
|
||||
for_each_active_iommu(iommu, drhd) {
|
||||
for_each_iommu(iommu, drhd) {
|
||||
if (pdev && segment != drhd->segment)
|
||||
continue;
|
||||
|
||||
|
@ -841,6 +881,9 @@ struct intel_iommu *device_to_iommu(struct device *dev, u8 *bus, u8 *devfn)
|
|||
}
|
||||
iommu = NULL;
|
||||
out:
|
||||
if (iommu_is_dummy(iommu, dev))
|
||||
iommu = NULL;
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return iommu;
|
||||
|
@ -2447,7 +2490,7 @@ struct dmar_domain *find_domain(struct device *dev)
|
|||
{
|
||||
struct device_domain_info *info;
|
||||
|
||||
if (unlikely(attach_deferred(dev) || iommu_dummy(dev)))
|
||||
if (unlikely(attach_deferred(dev)))
|
||||
return NULL;
|
||||
|
||||
/* No lock here, assumes no domain exit in normal case */
|
||||
|
@ -3989,35 +4032,6 @@ static void __init iommu_exit_mempool(void)
|
|||
iova_cache_put();
|
||||
}
|
||||
|
||||
static void quirk_ioat_snb_local_iommu(struct pci_dev *pdev)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd;
|
||||
u32 vtbar;
|
||||
int rc;
|
||||
|
||||
/* We know that this device on this chipset has its own IOMMU.
|
||||
* If we find it under a different IOMMU, then the BIOS is lying
|
||||
* to us. Hope that the IOMMU for this device is actually
|
||||
* disabled, and it needs no translation...
|
||||
*/
|
||||
rc = pci_bus_read_config_dword(pdev->bus, PCI_DEVFN(0, 0), 0xb0, &vtbar);
|
||||
if (rc) {
|
||||
/* "can't" happen */
|
||||
dev_info(&pdev->dev, "failed to run vt-d quirk\n");
|
||||
return;
|
||||
}
|
||||
vtbar &= 0xffff0000;
|
||||
|
||||
/* we know that the this iommu should be at offset 0xa000 from vtbar */
|
||||
drhd = dmar_find_matched_drhd_unit(pdev);
|
||||
if (!drhd || drhd->reg_base_addr - vtbar != 0xa000) {
|
||||
pr_warn_once(FW_BUG "BIOS assigned incorrect VT-d unit for Intel(R) QuickData Technology device\n");
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
|
||||
dev_iommu_priv_set(&pdev->dev, DUMMY_DEVICE_DOMAIN_INFO);
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IOAT_SNB, quirk_ioat_snb_local_iommu);
|
||||
|
||||
static void __init init_no_remapping_devices(void)
|
||||
{
|
||||
struct dmar_drhd_unit *drhd;
|
||||
|
@ -4049,12 +4063,8 @@ static void __init init_no_remapping_devices(void)
|
|||
/* This IOMMU has *only* gfx devices. Either bypass it or
|
||||
set the gfx_mapped flag, as appropriate */
|
||||
drhd->gfx_dedicated = 1;
|
||||
if (!dmar_map_gfx) {
|
||||
if (!dmar_map_gfx)
|
||||
drhd->ignored = 1;
|
||||
for_each_active_dev_scope(drhd->devices,
|
||||
drhd->devices_cnt, i, dev)
|
||||
dev_iommu_priv_set(dev, DUMMY_DEVICE_DOMAIN_INFO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue