diff options
author | Eric Biggers <ebiggers@google.com> | 2019-10-14 17:11:54 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-10-14 18:04:00 -0400 |
commit | 3c52b0af059e11a063970aed1ad143b9284a79c7 (patch) | |
tree | 3932cc5533532bd7a3398aa9d18413c68f1b4a29 | |
parent | e4f8e513c3d353c134ad4eef9fd0bba12406c7c8 (diff) |
lib/generic-radix-tree.c: add kmemleak annotations
Kmemleak is falsely reporting a leak of the slab allocation in
sctp_stream_init_ext():
BUG: memory leak
unreferenced object 0xffff8881114f5d80 (size 96):
comm "syz-executor934", pid 7160, jiffies 4294993058 (age 31.950s)
hex dump (first 32 bytes):
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace:
[<00000000ce7a1326>] kmemleak_alloc_recursive include/linux/kmemleak.h:55 [inline]
[<00000000ce7a1326>] slab_post_alloc_hook mm/slab.h:439 [inline]
[<00000000ce7a1326>] slab_alloc mm/slab.c:3326 [inline]
[<00000000ce7a1326>] kmem_cache_alloc_trace+0x13d/0x280 mm/slab.c:3553
[<000000007abb7ac9>] kmalloc include/linux/slab.h:547 [inline]
[<000000007abb7ac9>] kzalloc include/linux/slab.h:742 [inline]
[<000000007abb7ac9>] sctp_stream_init_ext+0x2b/0xa0 net/sctp/stream.c:157
[<0000000048ecb9c1>] sctp_sendmsg_to_asoc+0x946/0xa00 net/sctp/socket.c:1882
[<000000004483ca2b>] sctp_sendmsg+0x2a8/0x990 net/sctp/socket.c:2102
[...]
But it's freed later. Kmemleak misses the allocation because its
pointer is stored in the generic radix tree sctp_stream::out, and the
generic radix tree uses raw pages which aren't tracked by kmemleak.
Fix this by adding the kmemleak hooks to the generic radix tree code.
Link: http://lkml.kernel.org/r/20191004065039.727564-1-ebiggers@kernel.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
Reported-by: <syzbot+7f3b6b106be8dcdcdeec@syzkaller.appspotmail.com>
Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Cc: Kent Overstreet <kent.overstreet@gmail.com>
Cc: Vlad Yasevich <vyasevich@gmail.com>
Cc: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | lib/generic-radix-tree.c | 32 |
1 files changed, 26 insertions, 6 deletions
diff --git a/lib/generic-radix-tree.c b/lib/generic-radix-tree.c index ae25e2fa2187..f25eb111c051 100644 --- a/lib/generic-radix-tree.c +++ b/lib/generic-radix-tree.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <linux/export.h> | 2 | #include <linux/export.h> |
3 | #include <linux/generic-radix-tree.h> | 3 | #include <linux/generic-radix-tree.h> |
4 | #include <linux/gfp.h> | 4 | #include <linux/gfp.h> |
5 | #include <linux/kmemleak.h> | ||
5 | 6 | ||
6 | #define GENRADIX_ARY (PAGE_SIZE / sizeof(struct genradix_node *)) | 7 | #define GENRADIX_ARY (PAGE_SIZE / sizeof(struct genradix_node *)) |
7 | #define GENRADIX_ARY_SHIFT ilog2(GENRADIX_ARY) | 8 | #define GENRADIX_ARY_SHIFT ilog2(GENRADIX_ARY) |
@@ -75,6 +76,27 @@ void *__genradix_ptr(struct __genradix *radix, size_t offset) | |||
75 | } | 76 | } |
76 | EXPORT_SYMBOL(__genradix_ptr); | 77 | EXPORT_SYMBOL(__genradix_ptr); |
77 | 78 | ||
79 | static inline struct genradix_node *genradix_alloc_node(gfp_t gfp_mask) | ||
80 | { | ||
81 | struct genradix_node *node; | ||
82 | |||
83 | node = (struct genradix_node *)__get_free_page(gfp_mask|__GFP_ZERO); | ||
84 | |||
85 | /* | ||
86 | * We're using pages (not slab allocations) directly for kernel data | ||
87 | * structures, so we need to explicitly inform kmemleak of them in order | ||
88 | * to avoid false positive memory leak reports. | ||
89 | */ | ||
90 | kmemleak_alloc(node, PAGE_SIZE, 1, gfp_mask); | ||
91 | return node; | ||
92 | } | ||
93 | |||
94 | static inline void genradix_free_node(struct genradix_node *node) | ||
95 | { | ||
96 | kmemleak_free(node); | ||
97 | free_page((unsigned long)node); | ||
98 | } | ||
99 | |||
78 | /* | 100 | /* |
79 | * Returns pointer to the specified byte @offset within @radix, allocating it if | 101 | * Returns pointer to the specified byte @offset within @radix, allocating it if |
80 | * necessary - newly allocated slots are always zeroed out: | 102 | * necessary - newly allocated slots are always zeroed out: |
@@ -97,8 +119,7 @@ void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset, | |||
97 | break; | 119 | break; |
98 | 120 | ||
99 | if (!new_node) { | 121 | if (!new_node) { |
100 | new_node = (void *) | 122 | new_node = genradix_alloc_node(gfp_mask); |
101 | __get_free_page(gfp_mask|__GFP_ZERO); | ||
102 | if (!new_node) | 123 | if (!new_node) |
103 | return NULL; | 124 | return NULL; |
104 | } | 125 | } |
@@ -121,8 +142,7 @@ void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset, | |||
121 | n = READ_ONCE(*p); | 142 | n = READ_ONCE(*p); |
122 | if (!n) { | 143 | if (!n) { |
123 | if (!new_node) { | 144 | if (!new_node) { |
124 | new_node = (void *) | 145 | new_node = genradix_alloc_node(gfp_mask); |
125 | __get_free_page(gfp_mask|__GFP_ZERO); | ||
126 | if (!new_node) | 146 | if (!new_node) |
127 | return NULL; | 147 | return NULL; |
128 | } | 148 | } |
@@ -133,7 +153,7 @@ void *__genradix_ptr_alloc(struct __genradix *radix, size_t offset, | |||
133 | } | 153 | } |
134 | 154 | ||
135 | if (new_node) | 155 | if (new_node) |
136 | free_page((unsigned long) new_node); | 156 | genradix_free_node(new_node); |
137 | 157 | ||
138 | return &n->data[offset]; | 158 | return &n->data[offset]; |
139 | } | 159 | } |
@@ -191,7 +211,7 @@ static void genradix_free_recurse(struct genradix_node *n, unsigned level) | |||
191 | genradix_free_recurse(n->children[i], level - 1); | 211 | genradix_free_recurse(n->children[i], level - 1); |
192 | } | 212 | } |
193 | 213 | ||
194 | free_page((unsigned long) n); | 214 | genradix_free_node(n); |
195 | } | 215 | } |
196 | 216 | ||
197 | int __genradix_prealloc(struct __genradix *radix, size_t size, | 217 | int __genradix_prealloc(struct __genradix *radix, size_t size, |