Skip to content

Commit 5dd4019

Browse files
committed
netfilter: nf_tables: integrate pipapo into commit protocol
jira VULN-429 subsystem-sync netfilter:nf_tables 4.18.0-511 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit 212ed75 upstream-diff The offsets for the code are too divergent for the upstream patch to apply cleanly, but except for that fuzz I believe it is correct. The pipapo set backend follows copy-on-update approach, maintaining one clone of the existing datastructure that is being updated. The clone and current datastructures are swapped via rcu from the commit step. The existing integration with the commit protocol is flawed because there is no operation to clean up the clone if the transaction is aborted. Moreover, the datastructure swap happens on set element activation. This patch adds two new operations for sets: commit and abort, these new operations are invoked from the commit and abort steps, after the transactions have been digested, and it updates the pipapo set backend to use it. This patch adds a new ->pending_update field to sets to maintain a list of sets that require this new commit and abort operations. Fixes: 3c4287f ("nf_tables: Add set type for arbitrary concatenation of ranges") Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> (cherry picked from commit 212ed75) Signed-off-by: Greg Rose <g.v.rose@ciq.com>
1 parent bc45063 commit 5dd4019

File tree

3 files changed

+97
-15
lines changed

3 files changed

+97
-15
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,8 @@ struct nft_set_ops {
388388
const struct nft_set_elem *elem,
389389
unsigned int flags);
390390

391+
void (*commit)(const struct nft_set *set);
392+
void (*abort)(const struct nft_set *set);
391393
u64 (*privsize)(const struct nlattr * const nla[],
392394
const struct nft_set_desc *desc);
393395
bool (*estimate)(const struct nft_set_desc *desc,
@@ -489,6 +491,7 @@ struct nft_set {
489491
u16 policy;
490492
u16 udlen;
491493
unsigned char *udata;
494+
struct list_head pending_update;
492495
/* runtime data below here */
493496
const struct nft_set_ops *ops ____cacheline_aligned;
494497
u16 flags:14,

net/netfilter/nf_tables_api.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4311,6 +4311,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk,
43114311
}
43124312

43134313
set->handle = nf_tables_alloc_handle(table);
4314+
INIT_LIST_HEAD(&set->pending_update);
43144315

43154316
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
43164317
if (err < 0)
@@ -7876,9 +7877,24 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
78767877
}
78777878
}
78787879

7880+
static void nft_set_commit_update(struct list_head *set_update_list)
7881+
{
7882+
struct nft_set *set, *next;
7883+
7884+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
7885+
list_del_init(&set->pending_update);
7886+
7887+
if (!set->ops->commit)
7888+
continue;
7889+
7890+
set->ops->commit(set);
7891+
}
7892+
}
7893+
78797894
static int nf_tables_commit(struct net *net, struct sk_buff *skb)
78807895
{
78817896
struct nft_trans *trans, *next;
7897+
LIST_HEAD(set_update_list);
78827898
struct nft_trans_elem *te;
78837899
struct nft_chain *chain;
78847900
struct nft_table *table;
@@ -8023,6 +8039,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
80238039
nf_tables_setelem_notify(&trans->ctx, te->set,
80248040
&te->elem,
80258041
NFT_MSG_NEWSETELEM);
8042+
if (te->set->ops->commit &&
8043+
list_empty(&te->set->pending_update)) {
8044+
list_add_tail(&te->set->pending_update,
8045+
&set_update_list);
8046+
}
80268047
nft_trans_destroy(trans);
80278048
break;
80288049
case NFT_MSG_DELSETELEM:
@@ -8034,6 +8055,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
80348055
te->set->ops->remove(net, te->set, &te->elem);
80358056
atomic_dec(&te->set->nelems);
80368057
te->set->ndeact--;
8058+
if (te->set->ops->commit &&
8059+
list_empty(&te->set->pending_update)) {
8060+
list_add_tail(&te->set->pending_update,
8061+
&set_update_list);
8062+
}
80378063
break;
80388064
case NFT_MSG_NEWOBJ:
80398065
if (nft_trans_obj_update(trans)) {
@@ -8072,6 +8098,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
80728098
}
80738099
}
80748100

8101+
nft_set_commit_update(&set_update_list);
8102+
80758103
nft_commit_notify(net, NETLINK_CB(skb).portid);
80768104
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
80778105
nf_tables_commit_audit_log(&adl, net->nft.base_seq);
@@ -8124,9 +8152,24 @@ static void nf_tables_abort_release(struct nft_trans *trans)
81248152
kfree(trans);
81258153
}
81268154

8155+
static void nft_set_abort_update(struct list_head *set_update_list)
8156+
{
8157+
struct nft_set *set, *next;
8158+
8159+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
8160+
list_del_init(&set->pending_update);
8161+
8162+
if (!set->ops->abort)
8163+
continue;
8164+
8165+
set->ops->abort(set);
8166+
}
8167+
}
8168+
81278169
static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
81288170
{
81298171
struct nft_trans *trans, *next;
8172+
LIST_HEAD(set_update_list);
81308173
struct nft_trans_elem *te;
81318174

81328175
if (action == NFNL_ABORT_VALIDATE &&
@@ -8209,6 +8252,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
82098252
te = (struct nft_trans_elem *)trans->data;
82108253
te->set->ops->remove(net, te->set, &te->elem);
82118254
atomic_dec(&te->set->nelems);
8255+
8256+
if (te->set->ops->abort &&
8257+
list_empty(&te->set->pending_update)) {
8258+
list_add_tail(&te->set->pending_update,
8259+
&set_update_list);
8260+
}
82128261
break;
82138262
case NFT_MSG_DELSETELEM:
82148263
te = (struct nft_trans_elem *)trans->data;
@@ -8217,6 +8266,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
82178266
te->set->ops->activate(net, te->set, &te->elem);
82188267
te->set->ndeact--;
82198268

8269+
if (te->set->ops->abort &&
8270+
list_empty(&te->set->pending_update)) {
8271+
list_add_tail(&te->set->pending_update,
8272+
&set_update_list);
8273+
}
82208274
nft_trans_destroy(trans);
82218275
break;
82228276
case NFT_MSG_NEWOBJ:
@@ -8247,6 +8301,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
82478301
}
82488302
}
82498303

8304+
nft_set_abort_update(&set_update_list);
8305+
82508306
synchronize_rcu();
82518307

82528308
list_for_each_entry_safe_reverse(trans, next,

net/netfilter/nft_set_pipapo.c

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,17 +1495,10 @@ static void pipapo_free_fields(struct nft_pipapo_match *m)
14951495
}
14961496
}
14971497

1498-
/**
1499-
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1500-
* @rcu: RCU head
1501-
*/
1502-
static void pipapo_reclaim_match(struct rcu_head *rcu)
1498+
static void pipapo_free_match(struct nft_pipapo_match *m)
15031499
{
1504-
struct nft_pipapo_match *m;
15051500
int i;
15061501

1507-
m = container_of(rcu, struct nft_pipapo_match, rcu);
1508-
15091502
for_each_possible_cpu(i)
15101503
kfree(*per_cpu_ptr(m->scratch, i));
15111504

@@ -1517,7 +1510,19 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
15171510
}
15181511

15191512
/**
1520-
* pipapo_commit() - Replace lookup data with current working copy
1513+
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1514+
* @rcu: RCU head
1515+
*/
1516+
static void pipapo_reclaim_match(struct rcu_head *rcu)
1517+
{
1518+
struct nft_pipapo_match *m;
1519+
1520+
m = container_of(rcu, struct nft_pipapo_match, rcu);
1521+
pipapo_free_match(m);
1522+
}
1523+
1524+
/**
1525+
* nft_pipapo_commit() - Replace lookup data with current working copy
15211526
* @set: nftables API set representation
15221527
*
15231528
* While at it, check if we should perform garbage collection on the working
@@ -1527,7 +1532,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
15271532
* We also need to create a new working copy for subsequent insertions and
15281533
* deletions.
15291534
*/
1530-
static void pipapo_commit(const struct nft_set *set)
1535+
static void nft_pipapo_commit(const struct nft_set *set)
15311536
{
15321537
struct nft_pipapo *priv = nft_set_priv(set);
15331538
struct nft_pipapo_match *new_clone, *old;
@@ -1552,15 +1557,34 @@ static void pipapo_commit(const struct nft_set *set)
15521557
priv->clone = new_clone;
15531558
}
15541559

1560+
static void nft_pipapo_abort(const struct nft_set *set)
1561+
{
1562+
struct nft_pipapo *priv = nft_set_priv(set);
1563+
struct nft_pipapo_match *new_clone, *m;
1564+
1565+
if (!priv->dirty)
1566+
return;
1567+
1568+
m = rcu_dereference(priv->match);
1569+
1570+
new_clone = pipapo_clone(m);
1571+
if (IS_ERR(new_clone))
1572+
return;
1573+
1574+
priv->dirty = false;
1575+
1576+
pipapo_free_match(priv->clone);
1577+
priv->clone = new_clone;
1578+
}
1579+
15551580
/**
15561581
* nft_pipapo_activate() - Mark element reference as active given key, commit
15571582
* @net: Network namespace
15581583
* @set: nftables API set representation
15591584
* @elem: nftables API element representation containing key data
15601585
*
15611586
* On insertion, elements are added to a copy of the matching data currently
1562-
* in use for lookups, and not directly inserted into current lookup data, so
1563-
* we'll take care of that by calling pipapo_commit() here. Both
1587+
* in use for lookups, and not directly inserted into current lookup data. Both
15641588
* nft_pipapo_insert() and nft_pipapo_activate() are called once for each
15651589
* element, hence we can't purpose either one as a real commit operation.
15661590
*/
@@ -1576,8 +1600,6 @@ static void nft_pipapo_activate(const struct net *net,
15761600

15771601
nft_set_elem_change_active(net, set, &e->ext);
15781602
nft_set_elem_clear_busy(&e->ext);
1579-
1580-
pipapo_commit(set);
15811603
}
15821604

15831605
/**
@@ -1823,7 +1845,6 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set,
18231845
if (i == m->field_count) {
18241846
priv->dirty = true;
18251847
pipapo_drop(m, rulemap);
1826-
pipapo_commit(set);
18271848
return;
18281849
}
18291850

@@ -2132,6 +2153,8 @@ struct nft_set_type nft_set_pipapo_type __read_mostly = {
21322153
.init = nft_pipapo_init,
21332154
.destroy = nft_pipapo_destroy,
21342155
.gc_init = nft_pipapo_gc_init,
2156+
.commit = nft_pipapo_commit,
2157+
.abort = nft_pipapo_abort,
21352158
.elemsize = offsetof(struct nft_pipapo_elem, ext),
21362159
},
21372160
};

0 commit comments

Comments
 (0)