aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorMartin Wilck <martin.wilck@ts.fujitsu.com>2010-11-10 05:03:21 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2010-11-11 12:34:32 -0500
commit3b519e4ea618b6943a82931630872907f9ac2c2b (patch)
tree55ef8d5305a028dcada03945b3322143446ad9d0 /drivers/pci
parent4723d0f2f96e6c910f951d595067eb31e0dd2d01 (diff)
PCI: fix size checks for mmap() on /proc/bus/pci files
The checks for valid mmaps of PCI resources made through /proc/bus/pci files that were introduced in 9eff02e2042f96fb2aedd02e032eca1c5333d767 have several problems: 1. mmap() calls on /proc/bus/pci files are made with real file offsets > 0, whereas under /sys/bus/pci/devices, the start of the resource corresponds to offset 0. This may lead to false negatives in pci_mmap_fits(), which implicitly assumes the /sys/bus/pci/devices layout. 2. The loop in proc_bus_pci_mmap doesn't skip empty resouces. This leads to false positives, because pci_mmap_fits() doesn't treat empty resources correctly (the calculated size is 1 << (8*sizeof(resource_size_t)-PAGE_SHIFT) in this case!). 3. If a user maps resources with BAR > 0, pci_mmap_fits will emit bogus WARNINGS for the first resources that don't fit until the correct one is found. On many controllers the first 2-4 BARs are used, and the others are empty. In this case, an mmap attempt will first fail on the non-empty BARs (including the "right" BAR because of 1.) and emit bogus WARNINGS because of 3., and finally succeed on the first empty BAR because of 2. This is certainly not the intended behaviour. This patch addresses all 3 issues. Updated with an enum type for the additional parameter for pci_mmap_fits(). Cc: stable@kernel.org Signed-off-by: Martin Wilck <martin.wilck@ts.fujitsu.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-sysfs.c22
-rw-r--r--drivers/pci/pci.h7
-rw-r--r--drivers/pci/proc.c2
3 files changed, 23 insertions, 8 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index b5a7d9bfcb24..25accc9dda3b 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -705,17 +705,21 @@ void pci_remove_legacy_files(struct pci_bus *b)
705 705
706#ifdef HAVE_PCI_MMAP 706#ifdef HAVE_PCI_MMAP
707 707
708int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) 708int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
709 enum pci_mmap_api mmap_api)
709{ 710{
710 unsigned long nr, start, size; 711 unsigned long nr, start, size, pci_start;
711 712
713 if (pci_resource_len(pdev, resno) == 0)
714 return 0;
712 nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; 715 nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
713 start = vma->vm_pgoff; 716 start = vma->vm_pgoff;
714 size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; 717 size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
715 if (start < size && size - start >= nr) 718 pci_start = (mmap_api == PCI_MMAP_SYSFS) ?
719 pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0;
720 if (start >= pci_start && start < pci_start + size &&
721 start + nr <= pci_start + size)
716 return 1; 722 return 1;
717 WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n",
718 current->comm, start, start+nr, pci_name(pdev), resno, size);
719 return 0; 723 return 0;
720} 724}
721 725
@@ -745,8 +749,14 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
745 if (i >= PCI_ROM_RESOURCE) 749 if (i >= PCI_ROM_RESOURCE)
746 return -ENODEV; 750 return -ENODEV;
747 751
748 if (!pci_mmap_fits(pdev, i, vma)) 752 if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) {
753 WARN(1, "process \"%s\" tried to map 0x%08lx bytes "
754 "at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n",
755 current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff,
756 pci_name(pdev), i,
757 pci_resource_start(pdev, i), pci_resource_len(pdev, i));
749 return -EINVAL; 758 return -EINVAL;
759 }
750 760
751 /* pci_mmap_page_range() expects the same kind of entry as coming 761 /* pci_mmap_page_range() expects the same kind of entry as coming
752 * from /proc/bus/pci/ which is a "user visible" value. If this is 762 * from /proc/bus/pci/ which is a "user visible" value. If this is
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index f5c7c382765f..7d33f6673868 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -22,8 +22,13 @@ extern void pci_remove_firmware_label_files(struct pci_dev *pdev);
22#endif 22#endif
23extern void pci_cleanup_rom(struct pci_dev *dev); 23extern void pci_cleanup_rom(struct pci_dev *dev);
24#ifdef HAVE_PCI_MMAP 24#ifdef HAVE_PCI_MMAP
25enum pci_mmap_api {
26 PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */
27 PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */
28};
25extern int pci_mmap_fits(struct pci_dev *pdev, int resno, 29extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
26 struct vm_area_struct *vma); 30 struct vm_area_struct *vmai,
31 enum pci_mmap_api mmap_api);
27#endif 32#endif
28int pci_probe_reset_function(struct pci_dev *dev); 33int pci_probe_reset_function(struct pci_dev *dev);
29 34
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 297b72c880a1..ea00647f4732 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -257,7 +257,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma)
257 257
258 /* Make sure the caller is mapping a real resource for this device */ 258 /* Make sure the caller is mapping a real resource for this device */
259 for (i = 0; i < PCI_ROM_RESOURCE; i++) { 259 for (i = 0; i < PCI_ROM_RESOURCE; i++) {
260 if (pci_mmap_fits(dev, i, vma)) 260 if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS))
261 break; 261 break;
262 } 262 }
263 263