Skip to contentSkip to conversation

PCI: aer_inject: Don't call config read/write functions with invalid ops structure.

PATCH Applied David Daney 2 participants 2 messages Dec 22, 2015 – Feb 4, 2016 PCI

lore
Share & tools
lore
#0 lore raw

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
index 182224a..2d60e0e 100644
@@ -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
#1 lore raw
Quoted 12 lines · David Daney

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.

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 the pci_lock must already be held, and thus no other users of the bus->ops are possible.

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
index 182224a..2d60e0e 100644
@@ -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