aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiping Zhang <zlpnobody@gmail.com>2016-10-22 06:51:25 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2016-10-27 12:20:45 -0400
commit61f9e2924f4981d626b3a931fed935f2fa3cb4de (patch)
tree77d9f1c293690b43f9d858294e3164b464edd5fa
parentbb6a6e8e091353770074608c1d1bfde0e20b8154 (diff)
netfilter: nf_tables: fix *leak* when expr clone fail
When nft_expr_clone failed, a series of problems will happen: 1. module refcnt will leak, we call __module_get at the beginning but we forget to put it back if ops->clone returns fail 2. memory will be leaked, if clone fail, we just return NULL and forget to free the alloced element 3. set->nelems will become incorrect when set->size is specified. If clone fail, we should decrease the set->nelems Now this patch fixes these problems. And fortunately, clone fail will only happen on counter expression when memory is exhausted. Fixes: 086f332167d6 ("netfilter: nf_tables: add clone interface to expression operations") Signed-off-by: Liping Zhang <zlpnobody@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/net/netfilter/nf_tables.h6
-rw-r--r--net/netfilter/nf_tables_api.c11
-rw-r--r--net/netfilter/nft_dynset.c16
-rw-r--r--net/netfilter/nft_set_hash.c4
-rw-r--r--net/netfilter/nft_set_rbtree.c2
5 files changed, 23 insertions, 16 deletions
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 5031e072567b..741dcded5b4f 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -542,7 +542,8 @@ void *nft_set_elem_init(const struct nft_set *set,
542 const struct nft_set_ext_tmpl *tmpl, 542 const struct nft_set_ext_tmpl *tmpl,
543 const u32 *key, const u32 *data, 543 const u32 *key, const u32 *data,
544 u64 timeout, gfp_t gfp); 544 u64 timeout, gfp_t gfp);
545void nft_set_elem_destroy(const struct nft_set *set, void *elem); 545void nft_set_elem_destroy(const struct nft_set *set, void *elem,
546 bool destroy_expr);
546 547
547/** 548/**
548 * struct nft_set_gc_batch_head - nf_tables set garbage collection batch 549 * struct nft_set_gc_batch_head - nf_tables set garbage collection batch
@@ -693,7 +694,6 @@ static inline int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
693{ 694{
694 int err; 695 int err;
695 696
696 __module_get(src->ops->type->owner);
697 if (src->ops->clone) { 697 if (src->ops->clone) {
698 dst->ops = src->ops; 698 dst->ops = src->ops;
699 err = src->ops->clone(dst, src); 699 err = src->ops->clone(dst, src);
@@ -702,6 +702,8 @@ static inline int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
702 } else { 702 } else {
703 memcpy(dst, src, src->ops->size); 703 memcpy(dst, src, src->ops->size);
704 } 704 }
705
706 __module_get(src->ops->type->owner);
705 return 0; 707 return 0;
706} 708}
707 709
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 24db22257586..86e48aeb20be 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3452,14 +3452,15 @@ void *nft_set_elem_init(const struct nft_set *set,
3452 return elem; 3452 return elem;
3453} 3453}
3454 3454
3455void nft_set_elem_destroy(const struct nft_set *set, void *elem) 3455void nft_set_elem_destroy(const struct nft_set *set, void *elem,
3456 bool destroy_expr)
3456{ 3457{
3457 struct nft_set_ext *ext = nft_set_elem_ext(set, elem); 3458 struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
3458 3459
3459 nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE); 3460 nft_data_uninit(nft_set_ext_key(ext), NFT_DATA_VALUE);
3460 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) 3461 if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
3461 nft_data_uninit(nft_set_ext_data(ext), set->dtype); 3462 nft_data_uninit(nft_set_ext_data(ext), set->dtype);
3462 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPR)) 3463 if (destroy_expr && nft_set_ext_exists(ext, NFT_SET_EXT_EXPR))
3463 nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext)); 3464 nf_tables_expr_destroy(NULL, nft_set_ext_expr(ext));
3464 3465
3465 kfree(elem); 3466 kfree(elem);
@@ -3812,7 +3813,7 @@ void nft_set_gc_batch_release(struct rcu_head *rcu)
3812 3813
3813 gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu); 3814 gcb = container_of(rcu, struct nft_set_gc_batch, head.rcu);
3814 for (i = 0; i < gcb->head.cnt; i++) 3815 for (i = 0; i < gcb->head.cnt; i++)
3815 nft_set_elem_destroy(gcb->head.set, gcb->elems[i]); 3816 nft_set_elem_destroy(gcb->head.set, gcb->elems[i], true);
3816 kfree(gcb); 3817 kfree(gcb);
3817} 3818}
3818EXPORT_SYMBOL_GPL(nft_set_gc_batch_release); 3819EXPORT_SYMBOL_GPL(nft_set_gc_batch_release);
@@ -4030,7 +4031,7 @@ static void nf_tables_commit_release(struct nft_trans *trans)
4030 break; 4031 break;
4031 case NFT_MSG_DELSETELEM: 4032 case NFT_MSG_DELSETELEM:
4032 nft_set_elem_destroy(nft_trans_elem_set(trans), 4033 nft_set_elem_destroy(nft_trans_elem_set(trans),
4033 nft_trans_elem(trans).priv); 4034 nft_trans_elem(trans).priv, true);
4034 break; 4035 break;
4035 } 4036 }
4036 kfree(trans); 4037 kfree(trans);
@@ -4171,7 +4172,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
4171 break; 4172 break;
4172 case NFT_MSG_NEWSETELEM: 4173 case NFT_MSG_NEWSETELEM:
4173 nft_set_elem_destroy(nft_trans_elem_set(trans), 4174 nft_set_elem_destroy(nft_trans_elem_set(trans),
4174 nft_trans_elem(trans).priv); 4175 nft_trans_elem(trans).priv, true);
4175 break; 4176 break;
4176 } 4177 }
4177 kfree(trans); 4178 kfree(trans);
diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c
index bfdb689664b0..31ca94793aa9 100644
--- a/net/netfilter/nft_dynset.c
+++ b/net/netfilter/nft_dynset.c
@@ -44,18 +44,22 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
44 &regs->data[priv->sreg_key], 44 &regs->data[priv->sreg_key],
45 &regs->data[priv->sreg_data], 45 &regs->data[priv->sreg_data],
46 timeout, GFP_ATOMIC); 46 timeout, GFP_ATOMIC);
47 if (elem == NULL) { 47 if (elem == NULL)
48 if (set->size) 48 goto err1;
49 atomic_dec(&set->nelems);
50 return NULL;
51 }
52 49
53 ext = nft_set_elem_ext(set, elem); 50 ext = nft_set_elem_ext(set, elem);
54 if (priv->expr != NULL && 51 if (priv->expr != NULL &&
55 nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0) 52 nft_expr_clone(nft_set_ext_expr(ext), priv->expr) < 0)
56 return NULL; 53 goto err2;
57 54
58 return elem; 55 return elem;
56
57err2:
58 nft_set_elem_destroy(set, elem, false);
59err1:
60 if (set->size)
61 atomic_dec(&set->nelems);
62 return NULL;
59} 63}
60 64
61static void nft_dynset_eval(const struct nft_expr *expr, 65static void nft_dynset_eval(const struct nft_expr *expr,
diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c
index 3794cb2fc788..88d9fc8343e7 100644
--- a/net/netfilter/nft_set_hash.c
+++ b/net/netfilter/nft_set_hash.c
@@ -120,7 +120,7 @@ out:
120 return true; 120 return true;
121 121
122err2: 122err2:
123 nft_set_elem_destroy(set, he); 123 nft_set_elem_destroy(set, he, true);
124err1: 124err1:
125 return false; 125 return false;
126} 126}
@@ -332,7 +332,7 @@ static int nft_hash_init(const struct nft_set *set,
332 332
333static void nft_hash_elem_destroy(void *ptr, void *arg) 333static void nft_hash_elem_destroy(void *ptr, void *arg)
334{ 334{
335 nft_set_elem_destroy((const struct nft_set *)arg, ptr); 335 nft_set_elem_destroy((const struct nft_set *)arg, ptr, true);
336} 336}
337 337
338static void nft_hash_destroy(const struct nft_set *set) 338static void nft_hash_destroy(const struct nft_set *set)
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 38b5bda242f8..36493a7cae88 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -266,7 +266,7 @@ static void nft_rbtree_destroy(const struct nft_set *set)
266 while ((node = priv->root.rb_node) != NULL) { 266 while ((node = priv->root.rb_node) != NULL) {
267 rb_erase(node, &priv->root); 267 rb_erase(node, &priv->root);
268 rbe = rb_entry(node, struct nft_rbtree_elem, node); 268 rbe = rb_entry(node, struct nft_rbtree_elem, node);
269 nft_set_elem_destroy(set, rbe); 269 nft_set_elem_destroy(set, rbe, true);
270 } 270 }
271} 271}
272 272