4.9-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Pablo Neira Ayuso <[email protected]>


[ Upstream commit 35d0ac9070ef619e3bf44324375878a1c540387b ]

If the element exists and no NLM_F_EXCL is specified, do not bump
set->nelems, otherwise we leak one set element slot. This problem
amplifies if the set is full since the abort path always decrements the
counter for the -ENFILE case too, giving one spare extra slot.

Fix this by moving set->nelems update to nft_add_set_elem() after
successful element insertion. Moreover, remove the element if the set is
full so there is no need to rely on the abort path to undo things
anymore.

Fixes: c016c7e45ddf ("netfilter: nf_tables: honor NLM_F_EXCL flag in set 
element insertion")
Signed-off-by: Pablo Neira Ayuso <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
 net/netfilter/nf_tables_api.c |   16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3637,10 +3637,18 @@ static int nft_add_set_elem(struct nft_c
                goto err5;
        }
 
+       if (set->size &&
+           !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
+               err = -ENFILE;
+               goto err6;
+       }
+
        nft_trans_elem(trans) = elem;
        list_add_tail(&trans->list, &ctx->net->nft.commit_list);
        return 0;
 
+err6:
+       set->ops->remove(set, &elem);
 err5:
        kfree(trans);
 err4:
@@ -3687,15 +3695,9 @@ static int nf_tables_newsetelem(struct n
                return -EBUSY;
 
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
-               if (set->size &&
-                   !atomic_add_unless(&set->nelems, 1, set->size + 
set->ndeact))
-                       return -ENFILE;
-
                err = nft_add_set_elem(&ctx, set, attr, nlh->nlmsg_flags);
-               if (err < 0) {
-                       atomic_dec(&set->nelems);
+               if (err < 0)
                        break;
-               }
        }
        return err;
 }


Reply via email to