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 | ||
