diff options
author | Pekka Enberg <penberg@cs.helsinki.fi> | 2008-07-26 20:49:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-07-26 20:49:33 -0400 |
commit | 93bc4e89c260d91576840c4881d1066d84ccd422 (patch) | |
tree | 456176a054fc9a3fed18ac6ce50c7a34a86c5808 | |
parent | 3918fed5f31213067c1c345bd904e1ea369e6819 (diff) |
netfilter: fix double-free and use-after free
As suggested by Patrick McHardy, introduce a __krealloc() that doesn't
free the original buffer to fix a double-free and use-after-free bug
introduced by me in netfilter that uses RCU.
Reported-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: Pekka Enberg <penberg@cs.helsinki.fi>
Tested-by: Dieter Ries <clip2@gmx.de>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/slab.h | 1 | ||||
-rw-r--r-- | mm/util.c | 44 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_extend.c | 2 |
3 files changed, 36 insertions, 11 deletions
diff --git a/include/linux/slab.h b/include/linux/slab.h index 9aa90a6f20e0..be6f1d40b66a 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h | |||
@@ -96,6 +96,7 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr); | |||
96 | /* | 96 | /* |
97 | * Common kmalloc functions provided by all allocators | 97 | * Common kmalloc functions provided by all allocators |
98 | */ | 98 | */ |
99 | void * __must_check __krealloc(const void *, size_t, gfp_t); | ||
99 | void * __must_check krealloc(const void *, size_t, gfp_t); | 100 | void * __must_check krealloc(const void *, size_t, gfp_t); |
100 | void kfree(const void *); | 101 | void kfree(const void *); |
101 | size_t ksize(const void *); | 102 | size_t ksize(const void *); |
@@ -68,25 +68,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp) | |||
68 | EXPORT_SYMBOL(kmemdup); | 68 | EXPORT_SYMBOL(kmemdup); |
69 | 69 | ||
70 | /** | 70 | /** |
71 | * krealloc - reallocate memory. The contents will remain unchanged. | 71 | * __krealloc - like krealloc() but don't free @p. |
72 | * @p: object to reallocate memory for. | 72 | * @p: object to reallocate memory for. |
73 | * @new_size: how many bytes of memory are required. | 73 | * @new_size: how many bytes of memory are required. |
74 | * @flags: the type of memory to allocate. | 74 | * @flags: the type of memory to allocate. |
75 | * | 75 | * |
76 | * The contents of the object pointed to are preserved up to the | 76 | * This function is like krealloc() except it never frees the originally |
77 | * lesser of the new and old sizes. If @p is %NULL, krealloc() | 77 | * allocated buffer. Use this if you don't want to free the buffer immediately |
78 | * behaves exactly like kmalloc(). If @size is 0 and @p is not a | 78 | * like, for example, with RCU. |
79 | * %NULL pointer, the object pointed to is freed. | ||
80 | */ | 79 | */ |
81 | void *krealloc(const void *p, size_t new_size, gfp_t flags) | 80 | void *__krealloc(const void *p, size_t new_size, gfp_t flags) |
82 | { | 81 | { |
83 | void *ret; | 82 | void *ret; |
84 | size_t ks = 0; | 83 | size_t ks = 0; |
85 | 84 | ||
86 | if (unlikely(!new_size)) { | 85 | if (unlikely(!new_size)) |
87 | kfree(p); | ||
88 | return ZERO_SIZE_PTR; | 86 | return ZERO_SIZE_PTR; |
89 | } | ||
90 | 87 | ||
91 | if (p) | 88 | if (p) |
92 | ks = ksize(p); | 89 | ks = ksize(p); |
@@ -95,10 +92,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags) | |||
95 | return (void *)p; | 92 | return (void *)p; |
96 | 93 | ||
97 | ret = kmalloc_track_caller(new_size, flags); | 94 | ret = kmalloc_track_caller(new_size, flags); |
98 | if (ret && p) { | 95 | if (ret && p) |
99 | memcpy(ret, p, ks); | 96 | memcpy(ret, p, ks); |
97 | |||
98 | return ret; | ||
99 | } | ||
100 | EXPORT_SYMBOL(__krealloc); | ||
101 | |||
102 | /** | ||
103 | * krealloc - reallocate memory. The contents will remain unchanged. | ||
104 | * @p: object to reallocate memory for. | ||
105 | * @new_size: how many bytes of memory are required. | ||
106 | * @flags: the type of memory to allocate. | ||
107 | * | ||
108 | * The contents of the object pointed to are preserved up to the | ||
109 | * lesser of the new and old sizes. If @p is %NULL, krealloc() | ||
110 | * behaves exactly like kmalloc(). If @size is 0 and @p is not a | ||
111 | * %NULL pointer, the object pointed to is freed. | ||
112 | */ | ||
113 | void *krealloc(const void *p, size_t new_size, gfp_t flags) | ||
114 | { | ||
115 | void *ret; | ||
116 | |||
117 | if (unlikely(!new_size)) { | ||
100 | kfree(p); | 118 | kfree(p); |
119 | return ZERO_SIZE_PTR; | ||
101 | } | 120 | } |
121 | |||
122 | ret = __krealloc(p, new_size, flags); | ||
123 | if (ret && p != ret) | ||
124 | kfree(p); | ||
125 | |||
102 | return ret; | 126 | return ret; |
103 | } | 127 | } |
104 | EXPORT_SYMBOL(krealloc); | 128 | EXPORT_SYMBOL(krealloc); |
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 3469bc71a385..c956ef7eeecb 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c | |||
@@ -95,7 +95,7 @@ void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) | |||
95 | newlen = newoff + t->len; | 95 | newlen = newoff + t->len; |
96 | rcu_read_unlock(); | 96 | rcu_read_unlock(); |
97 | 97 | ||
98 | new = krealloc(ct->ext, newlen, gfp); | 98 | new = __krealloc(ct->ext, newlen, gfp); |
99 | if (!new) | 99 | if (!new) |
100 | return NULL; | 100 | return NULL; |
101 | 101 | ||