PCI: aer_inject: Don't call config read/write functions with invalid ops structure.
PATCH 1/1 PCI: aer_inject: Don't call config read/write functions with invalid ops structure.
From: David Daney <[email protected]>
The aer_inject module, when active, replaces the pci_ops structure of the bus so that it can intercept config space accesses. If the config space access needs to be forwarded to the original pci_ops, we must restore the bus's original ops, as the read()/write() functions may require use of the data in the pci_ops structure. An example of such functions are the pci_generic_config*() functions in drivers/pci/access.c. A failure to call these with a valid bus->ops results in dereferencing an invalid bus->ops->map_bus pointer and subsequent OOPs and system crash.
So, do as suggested above. When calling to the original read()/write() functions, temporarily switch back to the original pci_ops. This is safe to do as the pci_lock must already be held, and thus no other users of the bus->ops are possible.
--- drivers/pci/pcie/aer/aer_inject.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)
drivers/pci/pcie/aer/aer_inject.c +28 -2
@@ -188,7 +188,9 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,struct aer_error *err;unsigned long flags;struct pci_ops *ops; +struct pci_ops *my_ops;int domain; +int rv;spin_lock_irqsave (&inject_lock, flags);if (size !=sizeof (u32))
@@ -208,8 +210,19 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where, } out: 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); -return ops->read (bus, devfn, where, size, val); +return rv; } static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,
@@ -220,7 +233,9 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,unsigned long flags;int rw1cs;struct pci_ops *ops; +struct pci_ops *my_ops;int domain; +int rv;spin_lock_irqsave (&inject_lock, flags);if (size !=sizeof (u32))
@@ -243,8 +258,19 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, } out: 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); -return ops->write (bus, devfn, where, size, val); +return rv; }static struct pci_ops pci_ops_aer = {
Signature · 1 line
1.8.3.1
Bjorn Helgaas
AppliedQuoted 12 lines · David Daney
From: David Daney <[email protected]>
The
aer_injectmodule, when active, replaces thepci_opsstructure of the bus so that it can intercept config space accesses. If the config space access needs to be forwarded to the originalpci_ops, we must restore the bus's original ops, as the read()/write() functions may require use of the data in thepci_opsstructure. An example of such functions are the pci_generic_config*() functions indrivers/pci/access.c. A failure to call these with a validbus->opsresults in dereferencing an invalidbus->ops->map_buspointer and subsequent OOPs and system crash.
Quoted 6 lines
So, do as suggested above. When calling to the original read()/write() functions, temporarily switch back to the original
pci_ops. This is safe to do as thepci_lockmust already be held, and thus no other users of thebus->opsare possible.Signed-off-by David Daney <[email protected]>
Applied to pci/aer for v4.6, thanks, David!
Quoted 77 lines
--- drivers/pci/pcie/aer/aer_inject.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-)drivers/pci/pcie/aer/aer_inject.c +28
-2@@ -188,7 +188,9 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where,struct aer_error *err;unsigned long flags;struct pci_ops *ops; +struct pci_ops *my_ops;int domain; +int rv;spin_lock_irqsave (&inject_lock, flags);if (size !=sizeof (u32))@@ -208,8 +210,19 @@ static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where, } out: 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); -return ops->read (bus, devfn, where, size, val); +return rv; } static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,@@ -220,7 +233,9 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where,unsigned long flags;int rw1cs;struct pci_ops *ops; +struct pci_ops *my_ops;int domain; +int rv;spin_lock_irqsave (&inject_lock, flags);if (size !=sizeof (u32))@@ -243,8 +258,19 @@ static int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, } out: 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); -return ops->write (bus, devfn, where, size, val); +return rv; }static struct pci_ops pci_ops_aer = {Signature · 6 lines
1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html