aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
authorDavid Daney <david.daney@cavium.com>2015-12-22 16:44:51 -0500
committerBjorn Helgaas <bhelgaas@google.com>2016-02-04 15:26:27 -0500
commit7e8fbdc628760857369af05636ed4ddc4fc8569b (patch)
treeff25b6c3c10cad84a2b441c9532846756a4a7dc1 /drivers/pci/pcie
parent3b0a6d1a1b3335fde9cbbc659568ed619b07d24a (diff)
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: David Daney <david.daney@cavium.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c30
1 files changed, 28 insertions, 2 deletions
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 148a301a6b3c..79a5e112711a 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -188,7 +188,9 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
188 struct aer_error *err; 188 struct aer_error *err;
189 unsigned long flags; 189 unsigned long flags;
190 struct pci_ops *ops; 190 struct pci_ops *ops;
191 struct pci_ops *my_ops;
191 int domain; 192 int domain;
193 int rv;
192 194
193 spin_lock_irqsave(&inject_lock, flags); 195 spin_lock_irqsave(&inject_lock, flags);
194 if (size != sizeof(u32)) 196 if (size != sizeof(u32))
@@ -208,8 +210,19 @@ static int aer_inj_read_config(struct pci_bus *bus, unsigned int devfn,
208 } 210 }
209out: 211out:
210 ops = __find_pci_bus_ops(bus); 212 ops = __find_pci_bus_ops(bus);
213 /*
214 * pci_lock must already be held, so we can directly
215 * manipulate bus->ops. Many config access functions,
216 * including pci_generic_config_read() require the original
217 * bus->ops be installed to function, so temporarily put them
218 * back.
219 */
220 my_ops = bus->ops;
221 bus->ops = ops;
222 rv = ops->read(bus, devfn, where, size, val);
223 bus->ops = my_ops;
211 spin_unlock_irqrestore(&inject_lock, flags); 224 spin_unlock_irqrestore(&inject_lock, flags);
212 return ops->read(bus, devfn, where, size, val); 225 return rv;
213} 226}
214 227
215static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn, 228static 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 unsigned long flags; 233 unsigned long flags;
221 int rw1cs; 234 int rw1cs;
222 struct pci_ops *ops; 235 struct pci_ops *ops;
236 struct pci_ops *my_ops;
223 int domain; 237 int domain;
238 int rv;
224 239
225 spin_lock_irqsave(&inject_lock, flags); 240 spin_lock_irqsave(&inject_lock, flags);
226 if (size != sizeof(u32)) 241 if (size != sizeof(u32))
@@ -243,8 +258,19 @@ static int aer_inj_write_config(struct pci_bus *bus, unsigned int devfn,
243 } 258 }
244out: 259out:
245 ops = __find_pci_bus_ops(bus); 260 ops = __find_pci_bus_ops(bus);
261 /*
262 * pci_lock must already be held, so we can directly
263 * manipulate bus->ops. Many config access functions,
264 * including pci_generic_config_write() require the original
265 * bus->ops be installed to function, so temporarily put them
266 * back.
267 */
268 my_ops = bus->ops;
269 bus->ops = ops;
270 rv = ops->write(bus, devfn, where, size, val);
271 bus->ops = my_ops;
246 spin_unlock_irqrestore(&inject_lock, flags); 272 spin_unlock_irqrestore(&inject_lock, flags);
247 return ops->write(bus, devfn, where, size, val); 273 return rv;
248} 274}
249 275
250static struct pci_ops aer_inj_pci_ops = { 276static struct pci_ops aer_inj_pci_ops = {