On Sat, Jan 18, 2020 at 09:30:57PM +0100, Pablo Neira Ayuso wrote: > On Thu, Jan 16, 2020 at 10:11:09PM +0100, Florian Westphal wrote: > > Its possible to create tables in a family that isn't supported/known. > > Then, when adding a base chain, the table pointer can be NULL. > > > > This gets us a NULL ptr dereference in nf_tables_addchain(). > > > > Fixes: baae3e62f31618 ("netfilter: nf_tables: fix chain type module reference handling") > > Reported-by: syzbot+156a04714799b1d480bc@xxxxxxxxxxxxxxxxxxxxxxxxx > > Signed-off-by: Florian Westphal <fw@xxxxxxxxx> > > --- > > net/netfilter/nf_tables_api.c | 8 ++++++++ > > 1 file changed, 8 insertions(+) > > > > diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c > > index 65f51a2e9c2a..e8976128cdb1 100644 > > --- a/net/netfilter/nf_tables_api.c > > +++ b/net/netfilter/nf_tables_api.c > > @@ -953,6 +953,9 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, > > struct nft_ctx ctx; > > int err; > > > > + if (family >= NFPROTO_NUMPROTO) > > + return -EAFNOSUPPORT; > > + > > lockdep_assert_held(&net->nft.commit_mutex); > > attr = nla[NFTA_TABLE_NAME]; > > table = nft_table_lookup(net, attr, family, genmask); > > @@ -1765,6 +1768,9 @@ static int nft_chain_parse_hook(struct net *net, > > ha[NFTA_HOOK_PRIORITY] == NULL) > > return -EINVAL; > > > > + if (family >= NFPROTO_NUMPROTO) > > + return -EAFNOSUPPORT; > > + > > hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); > > hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); > > > > @@ -1774,6 +1780,8 @@ static int nft_chain_parse_hook(struct net *net, > > family, autoload); > > if (IS_ERR(type)) > > return PTR_ERR(type); > > + } else if (!type) { > > + return -EOPNOTSUPP; > > I think this check should be enough. > > I mean, NFPROTO_NUMPROTO still allows for creating tables for families > that don't exist (<= NFPROTO_NUMPROTO) and why bother on creating such > table. As long as such table does not crash the kernel, I think it's > fine. No changes can be attached anymore anyway. > > Otherwise, if a helper function to check for the families that are > really supported could be another alternative. But not sure it is > worth? Not worth. Probably this patch instead? Just make sure that access to the chain type array is safe, no direct access to chain_type[][] anymore. This includes the check for the default type too, since it cannot be assume to always have a filter chain for unsupported families. Thanks for explaining.
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 65f51a2e9c2a..4aa01c1253b1 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -553,14 +553,27 @@ static inline u64 nf_tables_alloc_handle(struct nft_table *table) static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX]; static const struct nft_chain_type * +__nft_chain_type_get(u8 family, enum nft_chain_types type) +{ + if (family >= NFPROTO_NUMPROTO || + type >= NFT_CHAIN_T_MAX) + return NULL; + + return chain_type[family][type]; +} + +static const struct nft_chain_type * __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family) { + const struct nft_chain_type *type; int i; for (i = 0; i < NFT_CHAIN_T_MAX; i++) { - if (chain_type[family][i] != NULL && - !nla_strcmp(nla, chain_type[family][i]->name)) - return chain_type[family][i]; + type = __nft_chain_type_get(family, i); + if (!type) + continue; + if (!nla_strcmp(nla, type->name)) + return type; } return NULL; } @@ -1162,11 +1175,8 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx) void nft_register_chain_type(const struct nft_chain_type *ctype) { - if (WARN_ON(ctype->family >= NFPROTO_NUMPROTO)) - return; - nfnl_lock(NFNL_SUBSYS_NFTABLES); - if (WARN_ON(chain_type[ctype->family][ctype->type] != NULL)) { + if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) { nfnl_unlock(NFNL_SUBSYS_NFTABLES); return; } @@ -1768,7 +1778,10 @@ static int nft_chain_parse_hook(struct net *net, hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); - type = chain_type[family][NFT_CHAIN_T_DEFAULT]; + type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT); + if (!type) + return -EOPNOTSUPP; + if (nla[NFTA_CHAIN_TYPE]) { type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE], family, autoload);