Skip to content

Commit b1a7f99

Browse files
Philipp Stannerbjorn-helgaas
authored andcommitted
PCI: Check BAR index for validity
Many functions in PCI use accessor macros such as pci_resource_len(), which take a BAR index. That index, however, is never checked for validity, potentially resulting in undefined behavior by overflowing the array pci_dev.resource in the macro pci_resource_n(). Since many users of those macros directly assign the accessed value to an unsigned integer, the macros cannot be changed easily anymore to return -EINVAL for invalid indexes. Consequently, the problem has to be mitigated in higher layers. Add pci_bar_index_valid(). Use it where appropriate. Link: https://lore.kernel.org/r/[email protected] Closes: https://lore.kernel.org/all/[email protected]/ Reported-by: Bingbu Cao <[email protected]> Signed-off-by: Philipp Stanner <[email protected]> [kwilczynski: correct if-statement condition the pci_bar_index_is_valid() helper function uses, tidy up code comments] Signed-off-by: Krzysztof Wilczyński <[email protected]> [bhelgaas: fix typo] Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent f09d393 commit b1a7f99

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed

drivers/pci/devres.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev,
577577
{
578578
void __iomem **legacy_iomap_table;
579579

580-
if (bar >= PCI_STD_NUM_BARS)
580+
if (!pci_bar_index_is_valid(bar))
581581
return -EINVAL;
582582

583583
legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);
@@ -622,7 +622,7 @@ static void pcim_remove_bar_from_legacy_table(struct pci_dev *pdev, int bar)
622622
{
623623
void __iomem **legacy_iomap_table;
624624

625-
if (bar >= PCI_STD_NUM_BARS)
625+
if (!pci_bar_index_is_valid(bar))
626626
return;
627627

628628
legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);
@@ -655,6 +655,9 @@ void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
655655
void __iomem *mapping;
656656
struct pcim_addr_devres *res;
657657

658+
if (!pci_bar_index_is_valid(bar))
659+
return NULL;
660+
658661
res = pcim_addr_devres_alloc(pdev);
659662
if (!res)
660663
return NULL;
@@ -722,6 +725,9 @@ void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar,
722725
int ret;
723726
struct pcim_addr_devres *res;
724727

728+
if (!pci_bar_index_is_valid(bar))
729+
return IOMEM_ERR_PTR(-EINVAL);
730+
725731
res = pcim_addr_devres_alloc(pdev);
726732
if (!res)
727733
return IOMEM_ERR_PTR(-ENOMEM);
@@ -823,6 +829,9 @@ static int _pcim_request_region(struct pci_dev *pdev, int bar, const char *name,
823829
int ret;
824830
struct pcim_addr_devres *res;
825831

832+
if (!pci_bar_index_is_valid(bar))
833+
return -EINVAL;
834+
826835
res = pcim_addr_devres_alloc(pdev);
827836
if (!res)
828837
return -ENOMEM;
@@ -991,6 +1000,9 @@ void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar,
9911000
void __iomem *mapping;
9921001
struct pcim_addr_devres *res;
9931002

1003+
if (!pci_bar_index_is_valid(bar))
1004+
return IOMEM_ERR_PTR(-EINVAL);
1005+
9941006
res = pcim_addr_devres_alloc(pdev);
9951007
if (!res)
9961008
return IOMEM_ERR_PTR(-ENOMEM);

drivers/pci/iomap.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
#include <linux/export.h>
1111

12+
#include "pci.h" /* for pci_bar_index_is_valid() */
13+
1214
/**
1315
* pci_iomap_range - create a virtual mapping cookie for a PCI BAR
1416
* @dev: PCI device that owns the BAR
@@ -33,12 +35,19 @@ void __iomem *pci_iomap_range(struct pci_dev *dev,
3335
unsigned long offset,
3436
unsigned long maxlen)
3537
{
36-
resource_size_t start = pci_resource_start(dev, bar);
37-
resource_size_t len = pci_resource_len(dev, bar);
38-
unsigned long flags = pci_resource_flags(dev, bar);
38+
resource_size_t start, len;
39+
unsigned long flags;
40+
41+
if (!pci_bar_index_is_valid(bar))
42+
return NULL;
43+
44+
start = pci_resource_start(dev, bar);
45+
len = pci_resource_len(dev, bar);
46+
flags = pci_resource_flags(dev, bar);
3947

4048
if (len <= offset || !start)
4149
return NULL;
50+
4251
len -= offset;
4352
start += offset;
4453
if (maxlen && len > maxlen)
@@ -77,16 +86,20 @@ void __iomem *pci_iomap_wc_range(struct pci_dev *dev,
7786
unsigned long offset,
7887
unsigned long maxlen)
7988
{
80-
resource_size_t start = pci_resource_start(dev, bar);
81-
resource_size_t len = pci_resource_len(dev, bar);
82-
unsigned long flags = pci_resource_flags(dev, bar);
89+
resource_size_t start, len;
90+
unsigned long flags;
8391

84-
85-
if (flags & IORESOURCE_IO)
92+
if (!pci_bar_index_is_valid(bar))
8693
return NULL;
8794

95+
start = pci_resource_start(dev, bar);
96+
len = pci_resource_len(dev, bar);
97+
flags = pci_resource_flags(dev, bar);
98+
8899
if (len <= offset || !start)
89100
return NULL;
101+
if (flags & IORESOURCE_IO)
102+
return NULL;
90103

91104
len -= offset;
92105
start += offset;

drivers/pci/pci.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3921,6 +3921,9 @@ EXPORT_SYMBOL(pci_enable_atomic_ops_to_root);
39213921
*/
39223922
void pci_release_region(struct pci_dev *pdev, int bar)
39233923
{
3924+
if (!pci_bar_index_is_valid(bar))
3925+
return;
3926+
39243927
/*
39253928
* This is done for backwards compatibility, because the old PCI devres
39263929
* API had a mode in which the function became managed if it had been
@@ -3965,6 +3968,9 @@ EXPORT_SYMBOL(pci_release_region);
39653968
static int __pci_request_region(struct pci_dev *pdev, int bar,
39663969
const char *name, int exclusive)
39673970
{
3971+
if (!pci_bar_index_is_valid(bar))
3972+
return -EINVAL;
3973+
39683974
if (pci_is_managed(pdev)) {
39693975
if (exclusive == IORESOURCE_EXCLUSIVE)
39703976
return pcim_request_region_exclusive(pdev, bar, name);

drivers/pci/pci.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,22 @@ static inline void pci_wakeup_event(struct pci_dev *dev)
167167
pm_wakeup_event(&dev->dev, 100);
168168
}
169169

170+
/**
171+
* pci_bar_index_is_valid - Check whether a BAR index is within valid range
172+
* @bar: BAR index
173+
*
174+
* Protects against overflowing &struct pci_dev.resource array.
175+
*
176+
* Return: true for valid index, false otherwise.
177+
*/
178+
static inline bool pci_bar_index_is_valid(int bar)
179+
{
180+
if (bar >= 0 && bar < PCI_NUM_RESOURCES)
181+
return true;
182+
183+
return false;
184+
}
185+
170186
static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
171187
{
172188
return !!(pci_dev->subordinate);

0 commit comments

Comments
 (0)