Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf

Pablo Neira Ayuso says:

====================
Netfilter fixes for net

The following patchset contains Netfilter patches for your net tree:

1) Fix NULL pointer dereference from nf_nat_decode_session() if NAT is
   not loaded, from Prashant Bhole.

2) Fix socket extension module autoload.

3) Don't bogusly reject sets with the NFT_SET_EVAL flag set on from
   the dynset extension.

4) Fix races with nf_tables module removal and netns exit path,
   patches from Florian Westphal.

5) Don't hit BUG_ON if jumpstack goes too deep, instead hit
   WARN_ON_ONCE, from Taehee Yoo.

6) Another NULL pointer dereference from ctnetlink, again if NAT is
   not loaded, from Florian Westphal.

7) Fix x_tables match list corruption in xt_connmark module removal
   path, also from Florian.

8) nf_conncount doesn't properly deal with conntrack zones, hence
   garbage collector may get rid of entries in a different zone.
   From Yi-Hung Wei.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-06-13 14:04:48 -07:00
commit 60d061e347
13 changed files with 52 additions and 23 deletions

View File

@ -345,7 +345,7 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family)
rcu_read_lock(); rcu_read_lock();
nat_hook = rcu_dereference(nf_nat_hook); nat_hook = rcu_dereference(nf_nat_hook);
if (nat_hook->decode_session) if (nat_hook && nat_hook->decode_session)
nat_hook->decode_session(skb, fl); nat_hook->decode_session(skb, fl);
rcu_read_unlock(); rcu_read_unlock();
#endif #endif

View File

@ -20,7 +20,8 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
bool *addit); bool *addit);
bool nf_conncount_add(struct hlist_head *head, bool nf_conncount_add(struct hlist_head *head,
const struct nf_conntrack_tuple *tuple); const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone);
void nf_conncount_cache_free(struct hlist_head *hhead); void nf_conncount_cache_free(struct hlist_head *hhead);

View File

@ -266,7 +266,7 @@ enum nft_rule_compat_attributes {
* @NFT_SET_INTERVAL: set contains intervals * @NFT_SET_INTERVAL: set contains intervals
* @NFT_SET_MAP: set is used as a dictionary * @NFT_SET_MAP: set is used as a dictionary
* @NFT_SET_TIMEOUT: set uses timeouts * @NFT_SET_TIMEOUT: set uses timeouts
* @NFT_SET_EVAL: set contains expressions for evaluation * @NFT_SET_EVAL: set can be updated from the evaluation path
* @NFT_SET_OBJECT: set contains stateful objects * @NFT_SET_OBJECT: set contains stateful objects
*/ */
enum nft_set_flags { enum nft_set_flags {

View File

@ -46,6 +46,7 @@
struct nf_conncount_tuple { struct nf_conncount_tuple {
struct hlist_node node; struct hlist_node node;
struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple tuple;
struct nf_conntrack_zone zone;
}; };
struct nf_conncount_rb { struct nf_conncount_rb {
@ -80,7 +81,8 @@ static int key_diff(const u32 *a, const u32 *b, unsigned int klen)
} }
bool nf_conncount_add(struct hlist_head *head, bool nf_conncount_add(struct hlist_head *head,
const struct nf_conntrack_tuple *tuple) const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone)
{ {
struct nf_conncount_tuple *conn; struct nf_conncount_tuple *conn;
@ -88,6 +90,7 @@ bool nf_conncount_add(struct hlist_head *head,
if (conn == NULL) if (conn == NULL)
return false; return false;
conn->tuple = *tuple; conn->tuple = *tuple;
conn->zone = *zone;
hlist_add_head(&conn->node, head); hlist_add_head(&conn->node, head);
return true; return true;
} }
@ -108,7 +111,7 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
/* check the saved connections */ /* check the saved connections */
hlist_for_each_entry_safe(conn, n, head, node) { hlist_for_each_entry_safe(conn, n, head, node) {
found = nf_conntrack_find_get(net, zone, &conn->tuple); found = nf_conntrack_find_get(net, &conn->zone, &conn->tuple);
if (found == NULL) { if (found == NULL) {
hlist_del(&conn->node); hlist_del(&conn->node);
kmem_cache_free(conncount_conn_cachep, conn); kmem_cache_free(conncount_conn_cachep, conn);
@ -117,7 +120,8 @@ unsigned int nf_conncount_lookup(struct net *net, struct hlist_head *head,
found_ct = nf_ct_tuplehash_to_ctrack(found); found_ct = nf_ct_tuplehash_to_ctrack(found);
if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple)) { if (tuple && nf_ct_tuple_equal(&conn->tuple, tuple) &&
nf_ct_zone_equal(found_ct, zone, zone->dir)) {
/* /*
* Just to be sure we have it only once in the list. * Just to be sure we have it only once in the list.
* We should not see tuples twice unless someone hooks * We should not see tuples twice unless someone hooks
@ -196,7 +200,7 @@ count_tree(struct net *net, struct rb_root *root,
if (!addit) if (!addit)
return count; return count;
if (!nf_conncount_add(&rbconn->hhead, tuple)) if (!nf_conncount_add(&rbconn->hhead, tuple, zone))
return 0; /* hotdrop */ return 0; /* hotdrop */
return count + 1; return count + 1;
@ -238,6 +242,7 @@ count_tree(struct net *net, struct rb_root *root,
} }
conn->tuple = *tuple; conn->tuple = *tuple;
conn->zone = *zone;
memcpy(rbconn->key, key, sizeof(u32) * keylen); memcpy(rbconn->key, key, sizeof(u32) * keylen);
INIT_HLIST_HEAD(&rbconn->hhead); INIT_HLIST_HEAD(&rbconn->hhead);

View File

@ -1446,7 +1446,8 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
} }
nfnl_lock(NFNL_SUBSYS_CTNETLINK); nfnl_lock(NFNL_SUBSYS_CTNETLINK);
rcu_read_lock(); rcu_read_lock();
if (nat_hook->parse_nat_setup) nat_hook = rcu_dereference(nf_nat_hook);
if (nat_hook)
return -EAGAIN; return -EAGAIN;
#endif #endif
return -EOPNOTSUPP; return -EOPNOTSUPP;

View File

@ -5837,18 +5837,23 @@ static int nf_tables_flowtable_event(struct notifier_block *this,
struct net_device *dev = netdev_notifier_info_to_dev(ptr); struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct nft_flowtable *flowtable; struct nft_flowtable *flowtable;
struct nft_table *table; struct nft_table *table;
struct net *net;
if (event != NETDEV_UNREGISTER) if (event != NETDEV_UNREGISTER)
return 0; return 0;
net = maybe_get_net(dev_net(dev));
if (!net)
return 0;
nfnl_lock(NFNL_SUBSYS_NFTABLES); nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_for_each_entry(table, &dev_net(dev)->nft.tables, list) { list_for_each_entry(table, &net->nft.tables, list) {
list_for_each_entry(flowtable, &table->flowtables, list) { list_for_each_entry(flowtable, &table->flowtables, list) {
nft_flowtable_event(event, dev, flowtable); nft_flowtable_event(event, dev, flowtable);
} }
} }
nfnl_unlock(NFNL_SUBSYS_NFTABLES); nfnl_unlock(NFNL_SUBSYS_NFTABLES);
put_net(net);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
@ -6439,7 +6444,7 @@ static void nf_tables_abort_release(struct nft_trans *trans)
kfree(trans); kfree(trans);
} }
static int nf_tables_abort(struct net *net, struct sk_buff *skb) static int __nf_tables_abort(struct net *net)
{ {
struct nft_trans *trans, *next; struct nft_trans *trans, *next;
struct nft_trans_elem *te; struct nft_trans_elem *te;
@ -6555,6 +6560,11 @@ static void nf_tables_cleanup(struct net *net)
nft_validate_state_update(net, NFT_VALIDATE_SKIP); nft_validate_state_update(net, NFT_VALIDATE_SKIP);
} }
static int nf_tables_abort(struct net *net, struct sk_buff *skb)
{
return __nf_tables_abort(net);
}
static bool nf_tables_valid_genid(struct net *net, u32 genid) static bool nf_tables_valid_genid(struct net *net, u32 genid)
{ {
return net->nft.base_seq == genid; return net->nft.base_seq == genid;
@ -7149,9 +7159,12 @@ static int __net_init nf_tables_init_net(struct net *net)
static void __net_exit nf_tables_exit_net(struct net *net) static void __net_exit nf_tables_exit_net(struct net *net)
{ {
nfnl_lock(NFNL_SUBSYS_NFTABLES);
if (!list_empty(&net->nft.commit_list))
__nf_tables_abort(net);
__nft_release_tables(net); __nft_release_tables(net);
nfnl_unlock(NFNL_SUBSYS_NFTABLES);
WARN_ON_ONCE(!list_empty(&net->nft.tables)); WARN_ON_ONCE(!list_empty(&net->nft.tables));
WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
} }
static struct pernet_operations nf_tables_net_ops = { static struct pernet_operations nf_tables_net_ops = {
@ -7193,13 +7206,13 @@ err1:
static void __exit nf_tables_module_exit(void) static void __exit nf_tables_module_exit(void)
{ {
unregister_pernet_subsys(&nf_tables_net_ops);
nfnetlink_subsys_unregister(&nf_tables_subsys); nfnetlink_subsys_unregister(&nf_tables_subsys);
unregister_netdevice_notifier(&nf_tables_flowtable_notifier); unregister_netdevice_notifier(&nf_tables_flowtable_notifier);
nft_chain_filter_fini();
unregister_pernet_subsys(&nf_tables_net_ops);
rcu_barrier(); rcu_barrier();
nf_tables_core_module_exit(); nf_tables_core_module_exit();
kfree(info); kfree(info);
nft_chain_filter_fini();
} }
module_init(nf_tables_module_init); module_init(nf_tables_module_init);

View File

@ -183,7 +183,8 @@ next_rule:
switch (regs.verdict.code) { switch (regs.verdict.code) {
case NFT_JUMP: case NFT_JUMP:
BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); if (WARN_ON_ONCE(stackptr >= NFT_JUMP_STACK_SIZE))
return NF_DROP;
jumpstack[stackptr].chain = chain; jumpstack[stackptr].chain = chain;
jumpstack[stackptr].rules = rules + 1; jumpstack[stackptr].rules = rules + 1;
stackptr++; stackptr++;

View File

@ -429,7 +429,7 @@ replay:
*/ */
if (err == -EAGAIN) { if (err == -EAGAIN) {
status |= NFNL_BATCH_REPLAY; status |= NFNL_BATCH_REPLAY;
goto next; goto done;
} }
} }
ack: ack:
@ -456,7 +456,7 @@ ack:
if (err) if (err)
status |= NFNL_BATCH_FAILURE; status |= NFNL_BATCH_FAILURE;
} }
next:
msglen = NLMSG_ALIGN(nlh->nlmsg_len); msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len) if (msglen > skb->len)
msglen = skb->len; msglen = skb->len;
@ -464,7 +464,11 @@ next:
} }
done: done:
if (status & NFNL_BATCH_REPLAY) { if (status & NFNL_BATCH_REPLAY) {
ss->abort(net, oskb); const struct nfnetlink_subsystem *ss2;
ss2 = nfnl_dereference_protected(subsys_id);
if (ss2 == ss)
ss->abort(net, oskb);
nfnl_err_reset(&err_list); nfnl_err_reset(&err_list);
nfnl_unlock(subsys_id); nfnl_unlock(subsys_id);
kfree_skb(skb); kfree_skb(skb);

View File

@ -318,6 +318,10 @@ static int nf_tables_netdev_event(struct notifier_block *this,
event != NETDEV_CHANGENAME) event != NETDEV_CHANGENAME)
return NOTIFY_DONE; return NOTIFY_DONE;
ctx.net = maybe_get_net(ctx.net);
if (!ctx.net)
return NOTIFY_DONE;
nfnl_lock(NFNL_SUBSYS_NFTABLES); nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_for_each_entry(table, &ctx.net->nft.tables, list) { list_for_each_entry(table, &ctx.net->nft.tables, list) {
if (table->family != NFPROTO_NETDEV) if (table->family != NFPROTO_NETDEV)
@ -334,6 +338,7 @@ static int nf_tables_netdev_event(struct notifier_block *this,
} }
} }
nfnl_unlock(NFNL_SUBSYS_NFTABLES); nfnl_unlock(NFNL_SUBSYS_NFTABLES);
put_net(ctx.net);
return NOTIFY_DONE; return NOTIFY_DONE;
} }

View File

@ -52,7 +52,7 @@ static inline void nft_connlimit_do_eval(struct nft_connlimit *priv,
if (!addit) if (!addit)
goto out; goto out;
if (!nf_conncount_add(&priv->hhead, tuple_ptr)) { if (!nf_conncount_add(&priv->hhead, tuple_ptr, zone)) {
regs->verdict.code = NF_DROP; regs->verdict.code = NF_DROP;
spin_unlock_bh(&priv->lock); spin_unlock_bh(&priv->lock);
return; return;

View File

@ -203,9 +203,7 @@ static int nft_dynset_init(const struct nft_ctx *ctx,
goto err1; goto err1;
set->ops->gc_init(set); set->ops->gc_init(set);
} }
}
} else if (set->flags & NFT_SET_EVAL)
return -EINVAL;
nft_set_ext_prepare(&priv->tmpl); nft_set_ext_prepare(&priv->tmpl);
nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen); nft_set_ext_add_length(&priv->tmpl, NFT_SET_EXT_KEY, set->klen);

View File

@ -142,3 +142,4 @@ module_exit(nft_socket_module_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Máté Eckl"); MODULE_AUTHOR("Máté Eckl");
MODULE_DESCRIPTION("nf_tables socket match module"); MODULE_DESCRIPTION("nf_tables socket match module");
MODULE_ALIAS_NFT_EXPR("socket");

View File

@ -211,7 +211,7 @@ static int __init connmark_mt_init(void)
static void __exit connmark_mt_exit(void) static void __exit connmark_mt_exit(void)
{ {
xt_unregister_match(&connmark_mt_reg); xt_unregister_match(&connmark_mt_reg);
xt_unregister_target(connmark_tg_reg); xt_unregister_targets(connmark_tg_reg, ARRAY_SIZE(connmark_tg_reg));
} }
module_init(connmark_mt_init); module_init(connmark_mt_init);