Skip to content

Commit dfc6126

Browse files
committed
netfilter: nf_tables: integrate pipapo into commit protocol
jira VULN-430 cve-pre CVE-2023-4244 commit-author Pablo Neira Ayuso <[email protected]> commit 212ed75 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 <[email protected]> (cherry picked from commit 212ed75) Signed-off-by: Marcin Wcisło <[email protected]>
1 parent fecc5cd commit dfc6126

File tree

3 files changed

+99
-16
lines changed

3 files changed

+99
-16
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,8 @@ struct nft_set_ops {
425425
const struct nft_set *set,
426426
const struct nft_set_elem *elem,
427427
unsigned int flags);
428-
428+
void (*commit)(const struct nft_set *set);
429+
void (*abort)(const struct nft_set *set);
429430
u64 (*privsize)(const struct nlattr * const nla[],
430431
const struct nft_set_desc *desc);
431432
bool (*estimate)(const struct nft_set_desc *desc,
@@ -520,6 +521,7 @@ struct nft_set {
520521
u16 policy;
521522
u16 udlen;
522523
unsigned char *udata;
524+
struct list_head pending_update;
523525
/* runtime data below here */
524526
const struct nft_set_ops *ops ____cacheline_aligned;
525527
u16 flags:14,

net/netfilter/nf_tables_api.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4760,6 +4760,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
47604760

47614761
set->num_exprs = num_exprs;
47624762
set->handle = nf_tables_alloc_handle(table);
4763+
INIT_LIST_HEAD(&set->pending_update);
47634764

47644765
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
47654766
if (err < 0)
@@ -9015,10 +9016,25 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
90159016
}
90169017
}
90179018

9019+
static void nft_set_commit_update(struct list_head *set_update_list)
9020+
{
9021+
struct nft_set *set, *next;
9022+
9023+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
9024+
list_del_init(&set->pending_update);
9025+
9026+
if (!set->ops->commit)
9027+
continue;
9028+
9029+
set->ops->commit(set);
9030+
}
9031+
}
9032+
90189033
static int nf_tables_commit(struct net *net, struct sk_buff *skb)
90199034
{
90209035
struct nftables_pernet *nft_net = nft_pernet(net);
90219036
struct nft_trans *trans, *next;
9037+
LIST_HEAD(set_update_list);
90229038
struct nft_trans_elem *te;
90239039
struct nft_chain *chain;
90249040
struct nft_table *table;
@@ -9181,6 +9197,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
91819197
nf_tables_setelem_notify(&trans->ctx, te->set,
91829198
&te->elem,
91839199
NFT_MSG_NEWSETELEM);
9200+
if (te->set->ops->commit &&
9201+
list_empty(&te->set->pending_update)) {
9202+
list_add_tail(&te->set->pending_update,
9203+
&set_update_list);
9204+
}
91849205
nft_trans_destroy(trans);
91859206
break;
91869207
case NFT_MSG_DELSETELEM:
@@ -9195,6 +9216,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
91959216
atomic_dec(&te->set->nelems);
91969217
te->set->ndeact--;
91979218
}
9219+
if (te->set->ops->commit &&
9220+
list_empty(&te->set->pending_update)) {
9221+
list_add_tail(&te->set->pending_update,
9222+
&set_update_list);
9223+
}
91989224
break;
91999225
case NFT_MSG_NEWOBJ:
92009226
if (nft_trans_obj_update(trans)) {
@@ -9257,6 +9283,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
92579283
}
92589284
}
92599285

9286+
nft_set_commit_update(&set_update_list);
9287+
92609288
nft_commit_notify(net, NETLINK_CB(skb).portid);
92619289
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
92629290
nf_tables_commit_audit_log(&adl, nft_net->base_seq);
@@ -9313,10 +9341,25 @@ static void nf_tables_abort_release(struct nft_trans *trans)
93139341
kfree(trans);
93149342
}
93159343

9344+
static void nft_set_abort_update(struct list_head *set_update_list)
9345+
{
9346+
struct nft_set *set, *next;
9347+
9348+
list_for_each_entry_safe(set, next, set_update_list, pending_update) {
9349+
list_del_init(&set->pending_update);
9350+
9351+
if (!set->ops->abort)
9352+
continue;
9353+
9354+
set->ops->abort(set);
9355+
}
9356+
}
9357+
93169358
static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
93179359
{
93189360
struct nftables_pernet *nft_net = nft_pernet(net);
93199361
struct nft_trans *trans, *next;
9362+
LIST_HEAD(set_update_list);
93209363
struct nft_trans_elem *te;
93219364

93229365
if (action == NFNL_ABORT_VALIDATE &&
@@ -9422,6 +9465,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
94229465
nft_setelem_remove(net, te->set, &te->elem);
94239466
if (!nft_setelem_is_catchall(te->set, &te->elem))
94249467
atomic_dec(&te->set->nelems);
9468+
9469+
if (te->set->ops->abort &&
9470+
list_empty(&te->set->pending_update)) {
9471+
list_add_tail(&te->set->pending_update,
9472+
&set_update_list);
9473+
}
94259474
break;
94269475
case NFT_MSG_DELSETELEM:
94279476
case NFT_MSG_DESTROYSETELEM:
@@ -9432,6 +9481,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
94329481
if (!nft_setelem_is_catchall(te->set, &te->elem))
94339482
te->set->ndeact--;
94349483

9484+
if (te->set->ops->abort &&
9485+
list_empty(&te->set->pending_update)) {
9486+
list_add_tail(&te->set->pending_update,
9487+
&set_update_list);
9488+
}
94359489
nft_trans_destroy(trans);
94369490
break;
94379491
case NFT_MSG_NEWOBJ:
@@ -9474,6 +9528,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
94749528
}
94759529
}
94769530

9531+
nft_set_abort_update(&set_update_list);
9532+
94779533
synchronize_rcu();
94789534

94799535
list_for_each_entry_safe_reverse(trans, next,

net/netfilter/nft_set_pipapo.c

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,17 +1600,10 @@ static void pipapo_free_fields(struct nft_pipapo_match *m)
16001600
}
16011601
}
16021602

1603-
/**
1604-
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1605-
* @rcu: RCU head
1606-
*/
1607-
static void pipapo_reclaim_match(struct rcu_head *rcu)
1603+
static void pipapo_free_match(struct nft_pipapo_match *m)
16081604
{
1609-
struct nft_pipapo_match *m;
16101605
int i;
16111606

1612-
m = container_of(rcu, struct nft_pipapo_match, rcu);
1613-
16141607
for_each_possible_cpu(i)
16151608
kfree(*per_cpu_ptr(m->scratch, i));
16161609

@@ -1625,7 +1618,19 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
16251618
}
16261619

16271620
/**
1628-
* pipapo_commit() - Replace lookup data with current working copy
1621+
* pipapo_reclaim_match - RCU callback to free fields from old matching data
1622+
* @rcu: RCU head
1623+
*/
1624+
static void pipapo_reclaim_match(struct rcu_head *rcu)
1625+
{
1626+
struct nft_pipapo_match *m;
1627+
1628+
m = container_of(rcu, struct nft_pipapo_match, rcu);
1629+
pipapo_free_match(m);
1630+
}
1631+
1632+
/**
1633+
* nft_pipapo_commit() - Replace lookup data with current working copy
16291634
* @set: nftables API set representation
16301635
*
16311636
* While at it, check if we should perform garbage collection on the working
@@ -1635,7 +1640,7 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
16351640
* We also need to create a new working copy for subsequent insertions and
16361641
* deletions.
16371642
*/
1638-
static void pipapo_commit(const struct nft_set *set)
1643+
static void nft_pipapo_commit(const struct nft_set *set)
16391644
{
16401645
struct nft_pipapo *priv = nft_set_priv(set);
16411646
struct nft_pipapo_match *new_clone, *old;
@@ -1660,15 +1665,34 @@ static void pipapo_commit(const struct nft_set *set)
16601665
priv->clone = new_clone;
16611666
}
16621667

1668+
static void nft_pipapo_abort(const struct nft_set *set)
1669+
{
1670+
struct nft_pipapo *priv = nft_set_priv(set);
1671+
struct nft_pipapo_match *new_clone, *m;
1672+
1673+
if (!priv->dirty)
1674+
return;
1675+
1676+
m = rcu_dereference(priv->match);
1677+
1678+
new_clone = pipapo_clone(m);
1679+
if (IS_ERR(new_clone))
1680+
return;
1681+
1682+
priv->dirty = false;
1683+
1684+
pipapo_free_match(priv->clone);
1685+
priv->clone = new_clone;
1686+
}
1687+
16631688
/**
16641689
* nft_pipapo_activate() - Mark element reference as active given key, commit
16651690
* @net: Network namespace
16661691
* @set: nftables API set representation
16671692
* @elem: nftables API element representation containing key data
16681693
*
16691694
* On insertion, elements are added to a copy of the matching data currently
1670-
* in use for lookups, and not directly inserted into current lookup data, so
1671-
* we'll take care of that by calling pipapo_commit() here. Both
1695+
* in use for lookups, and not directly inserted into current lookup data. Both
16721696
* nft_pipapo_insert() and nft_pipapo_activate() are called once for each
16731697
* element, hence we can't purpose either one as a real commit operation.
16741698
*/
@@ -1684,8 +1708,6 @@ static void nft_pipapo_activate(const struct net *net,
16841708

16851709
nft_set_elem_change_active(net, set, &e->ext);
16861710
nft_set_elem_clear_busy(&e->ext);
1687-
1688-
pipapo_commit(set);
16891711
}
16901712

16911713
/**
@@ -1935,7 +1957,6 @@ static void nft_pipapo_remove(const struct net *net, const struct nft_set *set,
19351957
if (i == m->field_count) {
19361958
priv->dirty = true;
19371959
pipapo_drop(m, rulemap);
1938-
pipapo_commit(set);
19391960
return;
19401961
}
19411962

@@ -2238,6 +2259,8 @@ const struct nft_set_type nft_set_pipapo_type = {
22382259
.init = nft_pipapo_init,
22392260
.destroy = nft_pipapo_destroy,
22402261
.gc_init = nft_pipapo_gc_init,
2262+
.commit = nft_pipapo_commit,
2263+
.abort = nft_pipapo_abort,
22412264
.elemsize = offsetof(struct nft_pipapo_elem, ext),
22422265
},
22432266
};
@@ -2260,6 +2283,8 @@ const struct nft_set_type nft_set_pipapo_avx2_type = {
22602283
.init = nft_pipapo_init,
22612284
.destroy = nft_pipapo_destroy,
22622285
.gc_init = nft_pipapo_gc_init,
2286+
.commit = nft_pipapo_commit,
2287+
.abort = nft_pipapo_abort,
22632288
.elemsize = offsetof(struct nft_pipapo_elem, ext),
22642289
},
22652290
};

0 commit comments

Comments
 (0)