]>
Commit | Line | Data |
---|---|---|
19fe9a56 GKH |
1 | From 3b519e4ea618b6943a82931630872907f9ac2c2b Mon Sep 17 00:00:00 2001 |
2 | From: Martin Wilck <martin.wilck@ts.fujitsu.com> | |
3 | Date: Wed, 10 Nov 2010 11:03:21 +0100 | |
4 | Subject: PCI: fix size checks for mmap() on /proc/bus/pci files | |
5 | ||
6 | From: Martin Wilck <martin.wilck@ts.fujitsu.com> | |
7 | ||
8 | commit 3b519e4ea618b6943a82931630872907f9ac2c2b upstream. | |
9 | ||
10 | The checks for valid mmaps of PCI resources made through /proc/bus/pci files | |
11 | that were introduced in 9eff02e2042f96fb2aedd02e032eca1c5333d767 have several | |
12 | problems: | |
13 | ||
14 | 1. mmap() calls on /proc/bus/pci files are made with real file offsets > 0, | |
15 | whereas under /sys/bus/pci/devices, the start of the resource corresponds | |
16 | to offset 0. This may lead to false negatives in pci_mmap_fits(), which | |
17 | implicitly assumes the /sys/bus/pci/devices layout. | |
18 | ||
19 | 2. The loop in proc_bus_pci_mmap doesn't skip empty resouces. This leads | |
20 | to false positives, because pci_mmap_fits() doesn't treat empty resources | |
21 | correctly (the calculated size is 1 << (8*sizeof(resource_size_t)-PAGE_SHIFT) | |
22 | in this case!). | |
23 | ||
24 | 3. If a user maps resources with BAR > 0, pci_mmap_fits will emit bogus | |
25 | WARNINGS for the first resources that don't fit until the correct one is found. | |
26 | ||
27 | On many controllers the first 2-4 BARs are used, and the others are empty. | |
28 | In this case, an mmap attempt will first fail on the non-empty BARs | |
29 | (including the "right" BAR because of 1.) and emit bogus WARNINGS because | |
30 | of 3., and finally succeed on the first empty BAR because of 2. | |
31 | This is certainly not the intended behaviour. | |
32 | ||
33 | This patch addresses all 3 issues. | |
34 | Updated with an enum type for the additional parameter for pci_mmap_fits(). | |
35 | ||
36 | Signed-off-by: Martin Wilck <martin.wilck@ts.fujitsu.com> | |
37 | Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> | |
38 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
39 | ||
40 | --- | |
41 | drivers/pci/pci-sysfs.c | 22 ++++++++++++++++------ | |
42 | drivers/pci/pci.h | 7 ++++++- | |
43 | drivers/pci/proc.c | 2 +- | |
44 | 3 files changed, 23 insertions(+), 8 deletions(-) | |
45 | ||
46 | --- a/drivers/pci/pci-sysfs.c | |
47 | +++ b/drivers/pci/pci-sysfs.c | |
48 | @@ -705,17 +705,21 @@ void pci_remove_legacy_files(struct pci_ | |
49 | ||
50 | #ifdef HAVE_PCI_MMAP | |
51 | ||
52 | -int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) | |
53 | +int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, | |
54 | + enum pci_mmap_api mmap_api) | |
55 | { | |
56 | - unsigned long nr, start, size; | |
57 | + unsigned long nr, start, size, pci_start; | |
58 | ||
59 | + if (pci_resource_len(pdev, resno) == 0) | |
60 | + return 0; | |
61 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | |
62 | start = vma->vm_pgoff; | |
63 | size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; | |
64 | - if (start < size && size - start >= nr) | |
65 | + pci_start = (mmap_api == PCI_MMAP_SYSFS) ? | |
66 | + pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; | |
67 | + if (start >= pci_start && start < pci_start + size && | |
68 | + start + nr <= pci_start + size) | |
69 | return 1; | |
70 | - WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n", | |
71 | - current->comm, start, start+nr, pci_name(pdev), resno, size); | |
72 | return 0; | |
73 | } | |
74 | ||
75 | @@ -745,8 +749,14 @@ pci_mmap_resource(struct kobject *kobj, | |
76 | if (i >= PCI_ROM_RESOURCE) | |
77 | return -ENODEV; | |
78 | ||
79 | - if (!pci_mmap_fits(pdev, i, vma)) | |
80 | + if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) { | |
81 | + WARN(1, "process \"%s\" tried to map 0x%08lx bytes " | |
82 | + "at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n", | |
83 | + current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff, | |
84 | + pci_name(pdev), i, | |
85 | + pci_resource_start(pdev, i), pci_resource_len(pdev, i)); | |
86 | return -EINVAL; | |
87 | + } | |
88 | ||
89 | /* pci_mmap_page_range() expects the same kind of entry as coming | |
90 | * from /proc/bus/pci/ which is a "user visible" value. If this is | |
91 | --- a/drivers/pci/pci.h | |
92 | +++ b/drivers/pci/pci.h | |
93 | @@ -22,8 +22,13 @@ extern void pci_remove_firmware_label_fi | |
94 | #endif | |
95 | extern void pci_cleanup_rom(struct pci_dev *dev); | |
96 | #ifdef HAVE_PCI_MMAP | |
97 | +enum pci_mmap_api { | |
98 | + PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ | |
99 | + PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */ | |
100 | +}; | |
101 | extern int pci_mmap_fits(struct pci_dev *pdev, int resno, | |
102 | - struct vm_area_struct *vma); | |
103 | + struct vm_area_struct *vmai, | |
104 | + enum pci_mmap_api mmap_api); | |
105 | #endif | |
106 | int pci_probe_reset_function(struct pci_dev *dev); | |
107 | ||
108 | --- a/drivers/pci/proc.c | |
109 | +++ b/drivers/pci/proc.c | |
110 | @@ -260,7 +260,7 @@ static int proc_bus_pci_mmap(struct file | |
111 | ||
112 | /* Make sure the caller is mapping a real resource for this device */ | |
113 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | |
114 | - if (pci_mmap_fits(dev, i, vma)) | |
115 | + if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) | |
116 | break; | |
117 | } | |
118 |