diff options
| author | Pekka Enberg <penberg@cs.helsinki.fi> | 2008-07-26 17:49:33 -0700 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2008-07-26 17:49:33 -0700 | 
| 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);  /*   * Common kmalloc functions provided by all allocators   */ +void * __must_check __krealloc(const void *, size_t, gfp_t);  void * __must_check krealloc(const void *, size_t, gfp_t);  void kfree(const void *);  size_t ksize(const void *); diff --git a/mm/util.c b/mm/util.c index 8f18683825bc..6ef9e9943f62 100644 --- a/mm/util.c +++ b/mm/util.c @@ -68,25 +68,22 @@ void *kmemdup(const void *src, size_t len, gfp_t gfp)  EXPORT_SYMBOL(kmemdup);  /** - * krealloc - reallocate memory. The contents will remain unchanged. + * __krealloc - like krealloc() but don't free @p.   * @p: object to reallocate memory for.   * @new_size: how many bytes of memory are required.   * @flags: the type of memory to allocate.   * - * The contents of the object pointed to are preserved up to the - * lesser of the new and old sizes.  If @p is %NULL, krealloc() - * behaves exactly like kmalloc().  If @size is 0 and @p is not a - * %NULL pointer, the object pointed to is freed. + * This function is like krealloc() except it never frees the originally + * allocated buffer. Use this if you don't want to free the buffer immediately + * like, for example, with RCU.   */ -void *krealloc(const void *p, size_t new_size, gfp_t flags) +void *__krealloc(const void *p, size_t new_size, gfp_t flags)  {  	void *ret;  	size_t ks = 0; -	if (unlikely(!new_size)) { -		kfree(p); +	if (unlikely(!new_size))  		return ZERO_SIZE_PTR; -	}  	if (p)  		ks = ksize(p); @@ -95,10 +92,37 @@ void *krealloc(const void *p, size_t new_size, gfp_t flags)  		return (void *)p;  	ret = kmalloc_track_caller(new_size, flags); -	if (ret && p) { +	if (ret && p)  		memcpy(ret, p, ks); + +	return ret; +} +EXPORT_SYMBOL(__krealloc); + +/** + * krealloc - reallocate memory. The contents will remain unchanged. + * @p: object to reallocate memory for. + * @new_size: how many bytes of memory are required. + * @flags: the type of memory to allocate. + * + * The contents of the object pointed to are preserved up to the + * lesser of the new and old sizes.  If @p is %NULL, krealloc() + * behaves exactly like kmalloc().  If @size is 0 and @p is not a + * %NULL pointer, the object pointed to is freed. + */ +void *krealloc(const void *p, size_t new_size, gfp_t flags) +{ +	void *ret; + +	if (unlikely(!new_size)) {  		kfree(p); +		return ZERO_SIZE_PTR;  	} + +	ret = __krealloc(p, new_size, flags); +	if (ret && p != ret) +		kfree(p); +  	return ret;  }  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)  	newlen = newoff + t->len;  	rcu_read_unlock(); -	new = krealloc(ct->ext, newlen, gfp); +	new = __krealloc(ct->ext, newlen, gfp);  	if (!new)  		return NULL;  | 
