Commit 7e8fbdc6 authored by David Daney's avatar David Daney Committed by Bjorn Helgaas

PCI/AER: Restore pci_ops pointer while calling original pci_ops

The aer_inject module intercepts config space accesses by replacing the
bus->ops pointer.  If it forwards accesses to the original pci_ops, and
those original ops use bus->ops, they see the aer_pci_ops instead of their
own pci_ops, which can cause a crash.

For example, pci_generic_config_read() uses the bus->ops->map_bus pointer.
If bus->ops is set to aer_pci_ops, which doesn't supply .map_bus,
pci_generic_config_read() will dereference an invalid pointer and cause a
crash.

Temporarily restore the original bus->ops pointer while calling ops->read()
or ops->write().  Callers of these functions already hold pci_lock, which
prevents other users of bus->ops until we're finished.

[bhelgaas: changelog]
Signed-off-by: default avatarDavid Daney <david.daney@cavium.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent 3b0a6d1a
...@@ -188,7 +188,9 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, ...@@ -188,7 +188,9 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
struct aer_error *err; struct aer_error *err;
unsigned long flags; unsigned long flags;
struct pci_ops *ops; struct pci_ops *ops;
struct pci_ops *my_ops;
int domain; int domain;
int rv;
spin_lock_irqsave(&inject_lock, flags); spin_lock_irqsave(&inject_lock, flags);
if (size != sizeof(u32)) if (size != sizeof(u32))
...@@ -208,8 +210,19 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn, ...@@ -208,8 +210,19 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
} }
out: out:
ops = __find_pci_bus_ops(bus); ops = __find_pci_bus_ops(bus);
/*
* pci_lock must already be held, so we can directly
* manipulate bus->ops. Many config access functions,
* including pci_generic_config_read() require the original
* bus->ops be installed to function, so temporarily put them
* back.
*/
my_ops = bus->ops;
bus->ops = ops;
rv = ops->read(bus, devfn, where, size, val);
bus->ops = my_ops;
spin_unlock_irqrestore(&inject_lock, flags); spin_unlock_irqrestore(&inject_lock, flags);
return ops->read(bus, devfn, where, size, val); return rv;
} }
static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
...@@ -220,7 +233,9 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, ...@@ -220,7 +233,9 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
unsigned long flags; unsigned long flags;
int rw1cs; int rw1cs;
struct pci_ops *ops; struct pci_ops *ops;
struct pci_ops *my_ops;
int domain; int domain;
int rv;
spin_lock_irqsave(&inject_lock, flags); spin_lock_irqsave(&inject_lock, flags);
if (size != sizeof(u32)) if (size != sizeof(u32))
...@@ -243,8 +258,19 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, ...@@ -243,8 +258,19 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
} }
out: out:
ops = __find_pci_bus_ops(bus); ops = __find_pci_bus_ops(bus);
/*
* pci_lock must already be held, so we can directly
* manipulate bus->ops. Many config access functions,
* including pci_generic_config_write() require the original
* bus->ops be installed to function, so temporarily put them
* back.
*/
my_ops = bus->ops;
bus->ops = ops;
rv = ops->write(bus, devfn, where, size, val);
bus->ops = my_ops;
spin_unlock_irqrestore(&inject_lock, flags); spin_unlock_irqrestore(&inject_lock, flags);
return ops->write(bus, devfn, where, size, val); return rv;
} }
static struct pci_ops aer_inj_pci_ops = { static struct pci_ops aer_inj_pci_ops = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment