netfilter: nf_tables: fix transaction race condition
[firefly-linux-kernel-4.4.55.git] / net / netfilter / nf_tables_api.c
index 199fd0f27b0e128cfb8674ca331c2dae240e1b1c..6fb532bf0fdb522a609d6b9182593344ec48f0a5 100644 (file)
@@ -227,7 +227,7 @@ nft_rule_deactivate_next(struct net *net, struct nft_rule *rule)
 
 static inline void nft_rule_clear(struct net *net, struct nft_rule *rule)
 {
-       rule->genmask = 0;
+       rule->genmask &= ~(1 << gencursor_next(net));
 }
 
 static int
@@ -3612,12 +3612,11 @@ static int nf_tables_commit(struct sk_buff *skb)
                                                 &te->elem,
                                                 NFT_MSG_DELSETELEM, 0);
                        te->set->ops->get(te->set, &te->elem);
-                       te->set->ops->remove(te->set, &te->elem);
                        nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
-                       if (te->elem.flags & NFT_SET_MAP) {
-                               nft_data_uninit(&te->elem.data,
-                                               te->set->dtype);
-                       }
+                       if (te->set->flags & NFT_SET_MAP &&
+                           !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
+                               nft_data_uninit(&te->elem.data, te->set->dtype);
+                       te->set->ops->remove(te->set, &te->elem);
                        nft_trans_destroy(trans);
                        break;
                }
@@ -3658,7 +3657,7 @@ static int nf_tables_abort(struct sk_buff *skb)
 {
        struct net *net = sock_net(skb->sk);
        struct nft_trans *trans, *next;
-       struct nft_set *set;
+       struct nft_trans_elem *te;
 
        list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
                switch (trans->msg_type) {
@@ -3719,9 +3718,13 @@ static int nf_tables_abort(struct sk_buff *skb)
                        break;
                case NFT_MSG_NEWSETELEM:
                        nft_trans_elem_set(trans)->nelems--;
-                       set = nft_trans_elem_set(trans);
-                       set->ops->get(set, &nft_trans_elem(trans));
-                       set->ops->remove(set, &nft_trans_elem(trans));
+                       te = (struct nft_trans_elem *)trans->data;
+                       te->set->ops->get(te->set, &te->elem);
+                       nft_data_uninit(&te->elem.key, NFT_DATA_VALUE);
+                       if (te->set->flags & NFT_SET_MAP &&
+                           !(te->elem.flags & NFT_SET_ELEM_INTERVAL_END))
+                               nft_data_uninit(&te->elem.data, te->set->dtype);
+                       te->set->ops->remove(te->set, &te->elem);
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM: