diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2016-10-11 16:55:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-10-11 18:06:33 -0400 |
commit | 9099daed9c6991a512c1f74b92ec49daf9408cda (patch) | |
tree | 9ebac94f168a0d5d843b715c1d19708d12a1c8c0 | |
parent | 0549a3c02efb350776bc869685a361045efd3a29 (diff) |
mm: kmemleak: avoid using __va() on addresses that don't have a lowmem mapping
Some of the kmemleak_*() callbacks in memblock, bootmem, CMA convert a
physical address to a virtual one using __va(). However, such physical
addresses may sometimes be located in highmem and using __va() is
incorrect, leading to inconsistent object tracking in kmemleak.
The following functions have been added to the kmemleak API and they take
a physical address as the object pointer. They only perform the
corresponding action if the address has a lowmem mapping:
kmemleak_alloc_phys
kmemleak_free_part_phys
kmemleak_not_leak_phys
kmemleak_ignore_phys
The affected calling places have been updated to use the new kmemleak
API.
Link: http://lkml.kernel.org/r/1471531432-16503-1-git-send-email-catalin.marinas@arm.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Reported-by: Vignesh R <vigneshr@ti.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | Documentation/dev-tools/kmemleak.rst | 9 | ||||
-rw-r--r-- | include/linux/kmemleak.h | 18 | ||||
-rw-r--r-- | mm/bootmem.c | 6 | ||||
-rw-r--r-- | mm/cma.c | 2 | ||||
-rw-r--r-- | mm/kmemleak.c | 47 | ||||
-rw-r--r-- | mm/memblock.c | 8 | ||||
-rw-r--r-- | mm/nobootmem.c | 2 |
7 files changed, 83 insertions, 9 deletions
diff --git a/Documentation/dev-tools/kmemleak.rst b/Documentation/dev-tools/kmemleak.rst index 1788722d5495..b2391b829169 100644 --- a/Documentation/dev-tools/kmemleak.rst +++ b/Documentation/dev-tools/kmemleak.rst | |||
@@ -162,6 +162,15 @@ See the include/linux/kmemleak.h header for the functions prototype. | |||
162 | - ``kmemleak_alloc_recursive`` - as kmemleak_alloc but checks the recursiveness | 162 | - ``kmemleak_alloc_recursive`` - as kmemleak_alloc but checks the recursiveness |
163 | - ``kmemleak_free_recursive`` - as kmemleak_free but checks the recursiveness | 163 | - ``kmemleak_free_recursive`` - as kmemleak_free but checks the recursiveness |
164 | 164 | ||
165 | The following functions take a physical address as the object pointer | ||
166 | and only perform the corresponding action if the address has a lowmem | ||
167 | mapping: | ||
168 | |||
169 | - ``kmemleak_alloc_phys`` | ||
170 | - ``kmemleak_free_part_phys`` | ||
171 | - ``kmemleak_not_leak_phys`` | ||
172 | - ``kmemleak_ignore_phys`` | ||
173 | |||
165 | Dealing with false positives/negatives | 174 | Dealing with false positives/negatives |
166 | -------------------------------------- | 175 | -------------------------------------- |
167 | 176 | ||
diff --git a/include/linux/kmemleak.h b/include/linux/kmemleak.h index 4894c6888bc6..1c2a32829620 100644 --- a/include/linux/kmemleak.h +++ b/include/linux/kmemleak.h | |||
@@ -38,6 +38,11 @@ extern void kmemleak_not_leak(const void *ptr) __ref; | |||
38 | extern void kmemleak_ignore(const void *ptr) __ref; | 38 | extern void kmemleak_ignore(const void *ptr) __ref; |
39 | extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; | 39 | extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref; |
40 | extern void kmemleak_no_scan(const void *ptr) __ref; | 40 | extern void kmemleak_no_scan(const void *ptr) __ref; |
41 | extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count, | ||
42 | gfp_t gfp) __ref; | ||
43 | extern void kmemleak_free_part_phys(phys_addr_t phys, size_t size) __ref; | ||
44 | extern void kmemleak_not_leak_phys(phys_addr_t phys) __ref; | ||
45 | extern void kmemleak_ignore_phys(phys_addr_t phys) __ref; | ||
41 | 46 | ||
42 | static inline void kmemleak_alloc_recursive(const void *ptr, size_t size, | 47 | static inline void kmemleak_alloc_recursive(const void *ptr, size_t size, |
43 | int min_count, unsigned long flags, | 48 | int min_count, unsigned long flags, |
@@ -106,6 +111,19 @@ static inline void kmemleak_erase(void **ptr) | |||
106 | static inline void kmemleak_no_scan(const void *ptr) | 111 | static inline void kmemleak_no_scan(const void *ptr) |
107 | { | 112 | { |
108 | } | 113 | } |
114 | static inline void kmemleak_alloc_phys(phys_addr_t phys, size_t size, | ||
115 | int min_count, gfp_t gfp) | ||
116 | { | ||
117 | } | ||
118 | static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size) | ||
119 | { | ||
120 | } | ||
121 | static inline void kmemleak_not_leak_phys(phys_addr_t phys) | ||
122 | { | ||
123 | } | ||
124 | static inline void kmemleak_ignore_phys(phys_addr_t phys) | ||
125 | { | ||
126 | } | ||
109 | 127 | ||
110 | #endif /* CONFIG_DEBUG_KMEMLEAK */ | 128 | #endif /* CONFIG_DEBUG_KMEMLEAK */ |
111 | 129 | ||
diff --git a/mm/bootmem.c b/mm/bootmem.c index a869f84f44d3..e8a55a3c9feb 100644 --- a/mm/bootmem.c +++ b/mm/bootmem.c | |||
@@ -155,7 +155,7 @@ void __init free_bootmem_late(unsigned long physaddr, unsigned long size) | |||
155 | { | 155 | { |
156 | unsigned long cursor, end; | 156 | unsigned long cursor, end; |
157 | 157 | ||
158 | kmemleak_free_part(__va(physaddr), size); | 158 | kmemleak_free_part_phys(physaddr, size); |
159 | 159 | ||
160 | cursor = PFN_UP(physaddr); | 160 | cursor = PFN_UP(physaddr); |
161 | end = PFN_DOWN(physaddr + size); | 161 | end = PFN_DOWN(physaddr + size); |
@@ -399,7 +399,7 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr, | |||
399 | { | 399 | { |
400 | unsigned long start, end; | 400 | unsigned long start, end; |
401 | 401 | ||
402 | kmemleak_free_part(__va(physaddr), size); | 402 | kmemleak_free_part_phys(physaddr, size); |
403 | 403 | ||
404 | start = PFN_UP(physaddr); | 404 | start = PFN_UP(physaddr); |
405 | end = PFN_DOWN(physaddr + size); | 405 | end = PFN_DOWN(physaddr + size); |
@@ -420,7 +420,7 @@ void __init free_bootmem(unsigned long physaddr, unsigned long size) | |||
420 | { | 420 | { |
421 | unsigned long start, end; | 421 | unsigned long start, end; |
422 | 422 | ||
423 | kmemleak_free_part(__va(physaddr), size); | 423 | kmemleak_free_part_phys(physaddr, size); |
424 | 424 | ||
425 | start = PFN_UP(physaddr); | 425 | start = PFN_UP(physaddr); |
426 | end = PFN_DOWN(physaddr + size); | 426 | end = PFN_DOWN(physaddr + size); |
@@ -336,7 +336,7 @@ int __init cma_declare_contiguous(phys_addr_t base, | |||
336 | * kmemleak scans/reads tracked objects for pointers to other | 336 | * kmemleak scans/reads tracked objects for pointers to other |
337 | * objects but this address isn't mapped and accessible | 337 | * objects but this address isn't mapped and accessible |
338 | */ | 338 | */ |
339 | kmemleak_ignore(phys_to_virt(addr)); | 339 | kmemleak_ignore_phys(addr); |
340 | base = addr; | 340 | base = addr; |
341 | } | 341 | } |
342 | 342 | ||
diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 086292f7c59d..a5e453cf05c4 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c | |||
@@ -90,6 +90,8 @@ | |||
90 | #include <linux/cache.h> | 90 | #include <linux/cache.h> |
91 | #include <linux/percpu.h> | 91 | #include <linux/percpu.h> |
92 | #include <linux/hardirq.h> | 92 | #include <linux/hardirq.h> |
93 | #include <linux/bootmem.h> | ||
94 | #include <linux/pfn.h> | ||
93 | #include <linux/mmzone.h> | 95 | #include <linux/mmzone.h> |
94 | #include <linux/slab.h> | 96 | #include <linux/slab.h> |
95 | #include <linux/thread_info.h> | 97 | #include <linux/thread_info.h> |
@@ -1121,6 +1123,51 @@ void __ref kmemleak_no_scan(const void *ptr) | |||
1121 | } | 1123 | } |
1122 | EXPORT_SYMBOL(kmemleak_no_scan); | 1124 | EXPORT_SYMBOL(kmemleak_no_scan); |
1123 | 1125 | ||
1126 | /** | ||
1127 | * kmemleak_alloc_phys - similar to kmemleak_alloc but taking a physical | ||
1128 | * address argument | ||
1129 | */ | ||
1130 | void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count, | ||
1131 | gfp_t gfp) | ||
1132 | { | ||
1133 | if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn) | ||
1134 | kmemleak_alloc(__va(phys), size, min_count, gfp); | ||
1135 | } | ||
1136 | EXPORT_SYMBOL(kmemleak_alloc_phys); | ||
1137 | |||
1138 | /** | ||
1139 | * kmemleak_free_part_phys - similar to kmemleak_free_part but taking a | ||
1140 | * physical address argument | ||
1141 | */ | ||
1142 | void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size) | ||
1143 | { | ||
1144 | if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn) | ||
1145 | kmemleak_free_part(__va(phys), size); | ||
1146 | } | ||
1147 | EXPORT_SYMBOL(kmemleak_free_part_phys); | ||
1148 | |||
1149 | /** | ||
1150 | * kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical | ||
1151 | * address argument | ||
1152 | */ | ||
1153 | void __ref kmemleak_not_leak_phys(phys_addr_t phys) | ||
1154 | { | ||
1155 | if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn) | ||
1156 | kmemleak_not_leak(__va(phys)); | ||
1157 | } | ||
1158 | EXPORT_SYMBOL(kmemleak_not_leak_phys); | ||
1159 | |||
1160 | /** | ||
1161 | * kmemleak_ignore_phys - similar to kmemleak_ignore but taking a physical | ||
1162 | * address argument | ||
1163 | */ | ||
1164 | void __ref kmemleak_ignore_phys(phys_addr_t phys) | ||
1165 | { | ||
1166 | if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn) | ||
1167 | kmemleak_ignore(__va(phys)); | ||
1168 | } | ||
1169 | EXPORT_SYMBOL(kmemleak_ignore_phys); | ||
1170 | |||
1124 | /* | 1171 | /* |
1125 | * Update an object's checksum and return true if it was modified. | 1172 | * Update an object's checksum and return true if it was modified. |
1126 | */ | 1173 | */ |
diff --git a/mm/memblock.c b/mm/memblock.c index c8dfa430342b..7608bc305936 100644 --- a/mm/memblock.c +++ b/mm/memblock.c | |||
@@ -723,7 +723,7 @@ int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size) | |||
723 | (unsigned long long)base + size - 1, | 723 | (unsigned long long)base + size - 1, |
724 | (void *)_RET_IP_); | 724 | (void *)_RET_IP_); |
725 | 725 | ||
726 | kmemleak_free_part(__va(base), size); | 726 | kmemleak_free_part_phys(base, size); |
727 | return memblock_remove_range(&memblock.reserved, base, size); | 727 | return memblock_remove_range(&memblock.reserved, base, size); |
728 | } | 728 | } |
729 | 729 | ||
@@ -1152,7 +1152,7 @@ static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size, | |||
1152 | * The min_count is set to 0 so that memblock allocations are | 1152 | * The min_count is set to 0 so that memblock allocations are |
1153 | * never reported as leaks. | 1153 | * never reported as leaks. |
1154 | */ | 1154 | */ |
1155 | kmemleak_alloc(__va(found), size, 0, 0); | 1155 | kmemleak_alloc_phys(found, size, 0, 0); |
1156 | return found; | 1156 | return found; |
1157 | } | 1157 | } |
1158 | return 0; | 1158 | return 0; |
@@ -1399,7 +1399,7 @@ void __init __memblock_free_early(phys_addr_t base, phys_addr_t size) | |||
1399 | memblock_dbg("%s: [%#016llx-%#016llx] %pF\n", | 1399 | memblock_dbg("%s: [%#016llx-%#016llx] %pF\n", |
1400 | __func__, (u64)base, (u64)base + size - 1, | 1400 | __func__, (u64)base, (u64)base + size - 1, |
1401 | (void *)_RET_IP_); | 1401 | (void *)_RET_IP_); |
1402 | kmemleak_free_part(__va(base), size); | 1402 | kmemleak_free_part_phys(base, size); |
1403 | memblock_remove_range(&memblock.reserved, base, size); | 1403 | memblock_remove_range(&memblock.reserved, base, size); |
1404 | } | 1404 | } |
1405 | 1405 | ||
@@ -1419,7 +1419,7 @@ void __init __memblock_free_late(phys_addr_t base, phys_addr_t size) | |||
1419 | memblock_dbg("%s: [%#016llx-%#016llx] %pF\n", | 1419 | memblock_dbg("%s: [%#016llx-%#016llx] %pF\n", |
1420 | __func__, (u64)base, (u64)base + size - 1, | 1420 | __func__, (u64)base, (u64)base + size - 1, |
1421 | (void *)_RET_IP_); | 1421 | (void *)_RET_IP_); |
1422 | kmemleak_free_part(__va(base), size); | 1422 | kmemleak_free_part_phys(base, size); |
1423 | cursor = PFN_UP(base); | 1423 | cursor = PFN_UP(base); |
1424 | end = PFN_DOWN(base + size); | 1424 | end = PFN_DOWN(base + size); |
1425 | 1425 | ||
diff --git a/mm/nobootmem.c b/mm/nobootmem.c index ba609b684d7a..487dad610731 100644 --- a/mm/nobootmem.c +++ b/mm/nobootmem.c | |||
@@ -84,7 +84,7 @@ void __init free_bootmem_late(unsigned long addr, unsigned long size) | |||
84 | { | 84 | { |
85 | unsigned long cursor, end; | 85 | unsigned long cursor, end; |
86 | 86 | ||
87 | kmemleak_free_part(__va(addr), size); | 87 | kmemleak_free_part_phys(addr, size); |
88 | 88 | ||
89 | cursor = PFN_UP(addr); | 89 | cursor = PFN_UP(addr); |
90 | end = PFN_DOWN(addr + size); | 90 | end = PFN_DOWN(addr + size); |