Commit 690bf643 authored by Jakub Kicinski's avatar Jakub Kicinski

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

Pablo Neira Ayuso says:

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

The following patchset contains Netfilter fixes for net:

1) Harden set element field checks to avoid out-of-bound memory access,
   this patch also fixes the type of issue described in 7e6bc1f6
   ("netfilter: nf_tables: stricter validation of element data") in a
   broader way.

2) Patches to restrict the chain, set, and rule id lookup in the
   transaction to the corresponding top-level table, patches from
   Thadeu Lima de Souza Cascardo.

3) Fix incorrect comment in ip6t_LOG.h

4) nft_data_init() performs upfront validation of the expected data.
   struct nft_data_desc is used to describe the expected data to be
   received from userspace. The .size field represents the maximum size
   that can be stored, for bound checks. Then, .len is an input/output field
   which stores the expected length as input (this is optional, to restrict
   the checks), as output it stores the real length received from userspace
   (if it was not specified as input). This patch comes in response to
   7e6bc1f6 ("netfilter: nf_tables: stricter validation of element data")
   to address this type of issue in a more generic way by avoid opencoded
   data validation. Next patch requires this as a dependency.

5) Disallow jump to implicit chain from set element, this configuration
   is invalid. Only allow jump to chain via immediate expression is
   supported at this stage.

6) Fix possible null-pointer derefence in the error path of table updates,
   if memory allocation of the transaction fails. From Florian Westphal.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: nf_tables: fix null deref due to zeroed list head
  netfilter: nf_tables: disallow jump to implicit chain from set element
  netfilter: nf_tables: upfront validation of data via nft_data_init()
  netfilter: ip6t_LOG: Fix a typo in a comment
  netfilter: nf_tables: do not allow RULE_ID to refer to another chain
  netfilter: nf_tables: do not allow CHAIN_ID to refer to another table
  netfilter: nf_tables: do not allow SET_ID to refer to another table
  netfilter: nf_tables: validate variable length element extension
====================

Link: https://lore.kernel.org/r/20220809220532.130240-1-pablo@netfilter.org/Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents bc3c8fe3 58007785
...@@ -221,13 +221,18 @@ struct nft_ctx { ...@@ -221,13 +221,18 @@ struct nft_ctx {
bool report; bool report;
}; };
enum nft_data_desc_flags {
NFT_DATA_DESC_SETELEM = (1 << 0),
};
struct nft_data_desc { struct nft_data_desc {
enum nft_data_types type; enum nft_data_types type;
unsigned int size;
unsigned int len; unsigned int len;
unsigned int flags;
}; };
int nft_data_init(const struct nft_ctx *ctx, int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
struct nft_data *data, unsigned int size,
struct nft_data_desc *desc, const struct nlattr *nla); struct nft_data_desc *desc, const struct nlattr *nla);
void nft_data_hold(const struct nft_data *data, enum nft_data_types type); void nft_data_hold(const struct nft_data *data, enum nft_data_types type);
void nft_data_release(const struct nft_data *data, enum nft_data_types type); void nft_data_release(const struct nft_data *data, enum nft_data_types type);
...@@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[]; ...@@ -651,6 +656,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[];
struct nft_set_ext_tmpl { struct nft_set_ext_tmpl {
u16 len; u16 len;
u8 offset[NFT_SET_EXT_NUM]; u8 offset[NFT_SET_EXT_NUM];
u8 ext_len[NFT_SET_EXT_NUM];
}; };
/** /**
...@@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id, ...@@ -680,7 +686,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id,
return -EINVAL; return -EINVAL;
tmpl->offset[id] = tmpl->len; tmpl->offset[id] = tmpl->len;
tmpl->len += nft_set_ext_types[id].len + len; tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
tmpl->len += tmpl->ext_len[id];
return 0; return 0;
} }
......
...@@ -17,4 +17,4 @@ struct ip6t_log_info { ...@@ -17,4 +17,4 @@ struct ip6t_log_info {
char prefix[30]; char prefix[30];
}; };
#endif /*_IPT_LOG_H*/ #endif /* _IP6T_LOG_H */
...@@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx, ...@@ -153,6 +153,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
if (trans == NULL) if (trans == NULL)
return NULL; return NULL;
INIT_LIST_HEAD(&trans->list);
trans->msg_type = msg_type; trans->msg_type = msg_type;
trans->ctx = *ctx; trans->ctx = *ctx;
...@@ -2472,6 +2473,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, ...@@ -2472,6 +2473,7 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
} }
static struct nft_chain *nft_chain_lookup_byid(const struct net *net, static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
const struct nft_table *table,
const struct nlattr *nla) const struct nlattr *nla)
{ {
struct nftables_pernet *nft_net = nft_pernet(net); struct nftables_pernet *nft_net = nft_pernet(net);
...@@ -2482,6 +2484,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net, ...@@ -2482,6 +2484,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
struct nft_chain *chain = trans->ctx.chain; struct nft_chain *chain = trans->ctx.chain;
if (trans->msg_type == NFT_MSG_NEWCHAIN && if (trans->msg_type == NFT_MSG_NEWCHAIN &&
chain->table == table &&
id == nft_trans_chain_id(trans)) id == nft_trans_chain_id(trans))
return chain; return chain;
} }
...@@ -3371,6 +3374,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table) ...@@ -3371,6 +3374,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
} }
static struct nft_rule *nft_rule_lookup_byid(const struct net *net, static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
const struct nft_chain *chain,
const struct nlattr *nla); const struct nlattr *nla);
#define NFT_RULE_MAXEXPRS 128 #define NFT_RULE_MAXEXPRS 128
...@@ -3417,7 +3421,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3417,7 +3421,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} else if (nla[NFTA_RULE_CHAIN_ID]) { } else if (nla[NFTA_RULE_CHAIN_ID]) {
chain = nft_chain_lookup_byid(net, nla[NFTA_RULE_CHAIN_ID]); chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]);
if (IS_ERR(chain)) { if (IS_ERR(chain)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]); NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
return PTR_ERR(chain); return PTR_ERR(chain);
...@@ -3459,7 +3463,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3459,7 +3463,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
return PTR_ERR(old_rule); return PTR_ERR(old_rule);
} }
} else if (nla[NFTA_RULE_POSITION_ID]) { } else if (nla[NFTA_RULE_POSITION_ID]) {
old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]); old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]);
if (IS_ERR(old_rule)) { if (IS_ERR(old_rule)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]); NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]);
return PTR_ERR(old_rule); return PTR_ERR(old_rule);
...@@ -3604,6 +3608,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3604,6 +3608,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
} }
static struct nft_rule *nft_rule_lookup_byid(const struct net *net, static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
const struct nft_chain *chain,
const struct nlattr *nla) const struct nlattr *nla)
{ {
struct nftables_pernet *nft_net = nft_pernet(net); struct nftables_pernet *nft_net = nft_pernet(net);
...@@ -3614,6 +3619,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net, ...@@ -3614,6 +3619,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
struct nft_rule *rule = nft_trans_rule(trans); struct nft_rule *rule = nft_trans_rule(trans);
if (trans->msg_type == NFT_MSG_NEWRULE && if (trans->msg_type == NFT_MSG_NEWRULE &&
trans->ctx.chain == chain &&
id == nft_trans_rule_id(trans)) id == nft_trans_rule_id(trans))
return rule; return rule;
} }
...@@ -3663,7 +3669,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info, ...@@ -3663,7 +3669,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
err = nft_delrule(&ctx, rule); err = nft_delrule(&ctx, rule);
} else if (nla[NFTA_RULE_ID]) { } else if (nla[NFTA_RULE_ID]) {
rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]); rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]);
if (IS_ERR(rule)) { if (IS_ERR(rule)) {
NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]); NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]);
return PTR_ERR(rule); return PTR_ERR(rule);
...@@ -3842,6 +3848,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table, ...@@ -3842,6 +3848,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table,
} }
static struct nft_set *nft_set_lookup_byid(const struct net *net, static struct nft_set *nft_set_lookup_byid(const struct net *net,
const struct nft_table *table,
const struct nlattr *nla, u8 genmask) const struct nlattr *nla, u8 genmask)
{ {
struct nftables_pernet *nft_net = nft_pernet(net); struct nftables_pernet *nft_net = nft_pernet(net);
...@@ -3853,6 +3860,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net, ...@@ -3853,6 +3860,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
struct nft_set *set = nft_trans_set(trans); struct nft_set *set = nft_trans_set(trans);
if (id == nft_trans_set_id(trans) && if (id == nft_trans_set_id(trans) &&
set->table == table &&
nft_active_genmask(set, genmask)) nft_active_genmask(set, genmask))
return set; return set;
} }
...@@ -3873,7 +3881,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net, ...@@ -3873,7 +3881,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
if (!nla_set_id) if (!nla_set_id)
return set; return set;
set = nft_set_lookup_byid(net, nla_set_id, genmask); set = nft_set_lookup_byid(net, table, nla_set_id, genmask);
} }
return set; return set;
} }
...@@ -5195,19 +5203,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set, ...@@ -5195,19 +5203,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set, static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
struct nft_data *key, struct nlattr *attr) struct nft_data *key, struct nlattr *attr)
{ {
struct nft_data_desc desc; struct nft_data_desc desc = {
int err; .type = NFT_DATA_VALUE,
.size = NFT_DATA_VALUE_MAXLEN,
err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr); .len = set->klen,
if (err < 0) };
return err;
if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
nft_data_release(key, desc.type);
return -EINVAL;
}
return 0; return nft_data_init(ctx, key, &desc, attr);
} }
static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
...@@ -5216,24 +5218,18 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set, ...@@ -5216,24 +5218,18 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
struct nlattr *attr) struct nlattr *attr)
{ {
u32 dtype; u32 dtype;
int err;
err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr);
if (err < 0)
return err;
if (set->dtype == NFT_DATA_VERDICT) if (set->dtype == NFT_DATA_VERDICT)
dtype = NFT_DATA_VERDICT; dtype = NFT_DATA_VERDICT;
else else
dtype = NFT_DATA_VALUE; dtype = NFT_DATA_VALUE;
if (dtype != desc->type || desc->type = dtype;
set->dlen != desc->len) { desc->size = NFT_DATA_VALUE_MAXLEN;
nft_data_release(data, desc->type); desc->len = set->dlen;
return -EINVAL; desc->flags = NFT_DATA_DESC_SETELEM;
}
return 0; return nft_data_init(ctx, data, desc, attr);
} }
static void *nft_setelem_catchall_get(const struct net *net, static void *nft_setelem_catchall_get(const struct net *net,
...@@ -5467,6 +5463,27 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx, ...@@ -5467,6 +5463,27 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
return ERR_PTR(err); return ERR_PTR(err);
} }
static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
{
len += nft_set_ext_types[id].len;
if (len > tmpl->ext_len[id] ||
len > U8_MAX)
return -1;
return 0;
}
static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
void *to, const void *from, u32 len)
{
if (nft_set_ext_check(tmpl, id, len) < 0)
return -1;
memcpy(to, from, len);
return 0;
}
void *nft_set_elem_init(const struct nft_set *set, void *nft_set_elem_init(const struct nft_set *set,
const struct nft_set_ext_tmpl *tmpl, const struct nft_set_ext_tmpl *tmpl,
const u32 *key, const u32 *key_end, const u32 *key, const u32 *key_end,
...@@ -5477,17 +5494,26 @@ void *nft_set_elem_init(const struct nft_set *set, ...@@ -5477,17 +5494,26 @@ void *nft_set_elem_init(const struct nft_set *set,
elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
if (elem == NULL) if (elem == NULL)
return NULL; return ERR_PTR(-ENOMEM);
ext = nft_set_elem_ext(set, elem); ext = nft_set_elem_ext(set, elem);
nft_set_ext_init(ext, tmpl); nft_set_ext_init(ext, tmpl);
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY)) if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
memcpy(nft_set_ext_key(ext), key, set->klen); nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) nft_set_ext_key(ext), key, set->klen) < 0)
memcpy(nft_set_ext_key_end(ext), key_end, set->klen); goto err_ext_check;
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
memcpy(nft_set_ext_data(ext), data, set->dlen); if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
nft_set_ext_key_end(ext), key_end, set->klen) < 0)
goto err_ext_check;
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
nft_set_ext_data(ext), data, set->dlen) < 0)
goto err_ext_check;
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
*nft_set_ext_expiration(ext) = get_jiffies_64() + expiration; *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
if (expiration == 0) if (expiration == 0)
...@@ -5497,6 +5523,11 @@ void *nft_set_elem_init(const struct nft_set *set, ...@@ -5497,6 +5523,11 @@ void *nft_set_elem_init(const struct nft_set *set,
*nft_set_ext_timeout(ext) = timeout; *nft_set_ext_timeout(ext) = timeout;
return elem; return elem;
err_ext_check:
kfree(elem);
return ERR_PTR(-EINVAL);
} }
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
...@@ -5584,14 +5615,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -5584,14 +5615,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
} }
static int nft_set_elem_expr_setup(struct nft_ctx *ctx, static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
const struct nft_set_ext_tmpl *tmpl,
const struct nft_set_ext *ext, const struct nft_set_ext *ext,
struct nft_expr *expr_array[], struct nft_expr *expr_array[],
u32 num_exprs) u32 num_exprs)
{ {
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
u32 len = sizeof(struct nft_set_elem_expr);
struct nft_expr *expr; struct nft_expr *expr;
int i, err; int i, err;
if (num_exprs == 0)
return 0;
for (i = 0; i < num_exprs; i++)
len += expr_array[i]->ops->size;
if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
return -EINVAL;
for (i = 0; i < num_exprs; i++) { for (i = 0; i < num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size); expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
err = nft_expr_clone(expr, expr_array[i]); err = nft_expr_clone(expr, expr_array[i]);
...@@ -6054,17 +6096,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -6054,17 +6096,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
} }
} }
err = -ENOMEM;
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
elem.key_end.val.data, elem.data.val.data, elem.key_end.val.data, elem.data.val.data,
timeout, expiration, GFP_KERNEL_ACCOUNT); timeout, expiration, GFP_KERNEL_ACCOUNT);
if (elem.priv == NULL) if (IS_ERR(elem.priv)) {
err = PTR_ERR(elem.priv);
goto err_parse_data; goto err_parse_data;
}
ext = nft_set_elem_ext(set, elem.priv); ext = nft_set_elem_ext(set, elem.priv);
if (flags) if (flags)
*nft_set_ext_flags(ext) = flags; *nft_set_ext_flags(ext) = flags;
if (ulen > 0) { if (ulen > 0) {
if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
err = -EINVAL;
goto err_elem_userdata;
}
udata = nft_set_ext_userdata(ext); udata = nft_set_ext_userdata(ext);
udata->len = ulen - 1; udata->len = ulen - 1;
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
...@@ -6073,14 +6121,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -6073,14 +6121,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
*nft_set_ext_obj(ext) = obj; *nft_set_ext_obj(ext) = obj;
obj->use++; obj->use++;
} }
err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs); err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
if (err < 0) if (err < 0)
goto err_elem_expr; goto err_elem_free;
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL) { if (trans == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err_elem_expr; goto err_elem_free;
} }
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
...@@ -6126,10 +6174,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -6126,10 +6174,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
nft_setelem_remove(ctx->net, set, &elem); nft_setelem_remove(ctx->net, set, &elem);
err_element_clash: err_element_clash:
kfree(trans); kfree(trans);
err_elem_expr: err_elem_free:
if (obj) if (obj)
obj->use--; obj->use--;
err_elem_userdata:
nf_tables_set_elem_destroy(ctx, set, elem.priv); nf_tables_set_elem_destroy(ctx, set, elem.priv);
err_parse_data: err_parse_data:
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
...@@ -6311,8 +6359,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set, ...@@ -6311,8 +6359,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
elem.key_end.val.data, NULL, 0, 0, elem.key_end.val.data, NULL, 0, 0,
GFP_KERNEL_ACCOUNT); GFP_KERNEL_ACCOUNT);
if (elem.priv == NULL) if (IS_ERR(elem.priv)) {
err = PTR_ERR(elem.priv);
goto fail_elem_key_end; goto fail_elem_key_end;
}
ext = nft_set_elem_ext(set, elem.priv); ext = nft_set_elem_ext(set, elem.priv);
if (flags) if (flags)
...@@ -9605,7 +9655,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, ...@@ -9605,7 +9655,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
tb[NFTA_VERDICT_CHAIN], tb[NFTA_VERDICT_CHAIN],
genmask); genmask);
} else if (tb[NFTA_VERDICT_CHAIN_ID]) { } else if (tb[NFTA_VERDICT_CHAIN_ID]) {
chain = nft_chain_lookup_byid(ctx->net, chain = nft_chain_lookup_byid(ctx->net, ctx->table,
tb[NFTA_VERDICT_CHAIN_ID]); tb[NFTA_VERDICT_CHAIN_ID]);
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
...@@ -9617,6 +9667,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, ...@@ -9617,6 +9667,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
return PTR_ERR(chain); return PTR_ERR(chain);
if (nft_is_base_chain(chain)) if (nft_is_base_chain(chain))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (desc->flags & NFT_DATA_DESC_SETELEM &&
chain->flags & NFT_CHAIN_BINDING)
return -EINVAL;
chain->use++; chain->use++;
data->verdict.chain = chain; data->verdict.chain = chain;
...@@ -9624,7 +9677,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, ...@@ -9624,7 +9677,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
} }
desc->len = sizeof(data->verdict); desc->len = sizeof(data->verdict);
desc->type = NFT_DATA_VERDICT;
return 0; return 0;
} }
...@@ -9677,20 +9730,25 @@ int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v) ...@@ -9677,20 +9730,25 @@ int nft_verdict_dump(struct sk_buff *skb, int type, const struct nft_verdict *v)
} }
static int nft_value_init(const struct nft_ctx *ctx, static int nft_value_init(const struct nft_ctx *ctx,
struct nft_data *data, unsigned int size, struct nft_data *data, struct nft_data_desc *desc,
struct nft_data_desc *desc, const struct nlattr *nla) const struct nlattr *nla)
{ {
unsigned int len; unsigned int len;
len = nla_len(nla); len = nla_len(nla);
if (len == 0) if (len == 0)
return -EINVAL; return -EINVAL;
if (len > size) if (len > desc->size)
return -EOVERFLOW; return -EOVERFLOW;
if (desc->len) {
if (len != desc->len)
return -EINVAL;
} else {
desc->len = len;
}
nla_memcpy(data->data, nla, len); nla_memcpy(data->data, nla, len);
desc->type = NFT_DATA_VALUE;
desc->len = len;
return 0; return 0;
} }
...@@ -9710,7 +9768,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { ...@@ -9710,7 +9768,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
* *
* @ctx: context of the expression using the data * @ctx: context of the expression using the data
* @data: destination struct nft_data * @data: destination struct nft_data
* @size: maximum data length
* @desc: data description * @desc: data description
* @nla: netlink attribute containing data * @nla: netlink attribute containing data
* *
...@@ -9720,24 +9777,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { ...@@ -9720,24 +9777,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
* The caller can indicate that it only wants to accept data of type * The caller can indicate that it only wants to accept data of type
* NFT_DATA_VALUE by passing NULL for the ctx argument. * NFT_DATA_VALUE by passing NULL for the ctx argument.
*/ */
int nft_data_init(const struct nft_ctx *ctx, int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
struct nft_data *data, unsigned int size,
struct nft_data_desc *desc, const struct nlattr *nla) struct nft_data_desc *desc, const struct nlattr *nla)
{ {
struct nlattr *tb[NFTA_DATA_MAX + 1]; struct nlattr *tb[NFTA_DATA_MAX + 1];
int err; int err;
if (WARN_ON_ONCE(!desc->size))
return -EINVAL;
err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla, err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla,
nft_data_policy, NULL); nft_data_policy, NULL);
if (err < 0) if (err < 0)
return err; return err;
if (tb[NFTA_DATA_VALUE]) if (tb[NFTA_DATA_VALUE]) {
return nft_value_init(ctx, data, size, desc, if (desc->type != NFT_DATA_VALUE)
tb[NFTA_DATA_VALUE]); return -EINVAL;
if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
return -EINVAL; } else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) {
if (desc->type != NFT_DATA_VERDICT)
return -EINVAL;
err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
} else {
err = -EINVAL;
}
return err;
} }
EXPORT_SYMBOL_GPL(nft_data_init); EXPORT_SYMBOL_GPL(nft_data_init);
......
...@@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { ...@@ -93,7 +93,16 @@ static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = {
static int nft_bitwise_init_bool(struct nft_bitwise *priv, static int nft_bitwise_init_bool(struct nft_bitwise *priv,
const struct nlattr *const tb[]) const struct nlattr *const tb[])
{ {
struct nft_data_desc mask, xor; struct nft_data_desc mask = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->mask),
.len = priv->len,
};
struct nft_data_desc xor = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->xor),
.len = priv->len,
};
int err; int err;
if (tb[NFTA_BITWISE_DATA]) if (tb[NFTA_BITWISE_DATA])
...@@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv, ...@@ -103,37 +112,30 @@ static int nft_bitwise_init_bool(struct nft_bitwise *priv,
!tb[NFTA_BITWISE_XOR]) !tb[NFTA_BITWISE_XOR])
return -EINVAL; return -EINVAL;
err = nft_data_init(NULL, &priv->mask, sizeof(priv->mask), &mask, err = nft_data_init(NULL, &priv->mask, &mask, tb[NFTA_BITWISE_MASK]);
tb[NFTA_BITWISE_MASK]);
if (err < 0) if (err < 0)
return err; return err;
if (mask.type != NFT_DATA_VALUE || mask.len != priv->len) {
err = -EINVAL;
goto err_mask_release;
}
err = nft_data_init(NULL, &priv->xor, sizeof(priv->xor), &xor, err = nft_data_init(NULL, &priv->xor, &xor, tb[NFTA_BITWISE_XOR]);
tb[NFTA_BITWISE_XOR]);
if (err < 0) if (err < 0)
goto err_mask_release; goto err_xor_err;
if (xor.type != NFT_DATA_VALUE || xor.len != priv->len) {
err = -EINVAL;
goto err_xor_release;
}
return 0; return 0;
err_xor_release: err_xor_err:
nft_data_release(&priv->xor, xor.type);
err_mask_release:
nft_data_release(&priv->mask, mask.type); nft_data_release(&priv->mask, mask.type);
return err; return err;
} }
static int nft_bitwise_init_shift(struct nft_bitwise *priv, static int nft_bitwise_init_shift(struct nft_bitwise *priv,
const struct nlattr *const tb[]) const struct nlattr *const tb[])
{ {
struct nft_data_desc d; struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
.len = sizeof(u32),
};
int err; int err;
if (tb[NFTA_BITWISE_MASK] || if (tb[NFTA_BITWISE_MASK] ||
...@@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv, ...@@ -143,13 +145,12 @@ static int nft_bitwise_init_shift(struct nft_bitwise *priv,
if (!tb[NFTA_BITWISE_DATA]) if (!tb[NFTA_BITWISE_DATA])
return -EINVAL; return -EINVAL;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &d, err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_BITWISE_DATA]);
tb[NFTA_BITWISE_DATA]);
if (err < 0) if (err < 0)
return err; return err;
if (d.type != NFT_DATA_VALUE || d.len != sizeof(u32) ||
priv->data.data[0] >= BITS_PER_TYPE(u32)) { if (priv->data.data[0] >= BITS_PER_TYPE(u32)) {
nft_data_release(&priv->data, d.type); nft_data_release(&priv->data, desc.type);
return -EINVAL; return -EINVAL;
} }
...@@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = { ...@@ -339,22 +340,21 @@ static const struct nft_expr_ops nft_bitwise_ops = {
static int static int
nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out) nft_bitwise_extract_u32_data(const struct nlattr * const tb, u32 *out)
{ {
struct nft_data_desc desc;
struct nft_data data; struct nft_data data;
int err = 0; struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
.len = sizeof(u32),
};
int err;
err = nft_data_init(NULL, &data, sizeof(data), &desc, tb); err = nft_data_init(NULL, &data, &desc, tb);
if (err < 0) if (err < 0)
return err; return err;
if (desc.type != NFT_DATA_VALUE || desc.len != sizeof(u32)) {
err = -EINVAL;
goto err;
}
*out = data.data[0]; *out = data.data[0];
err:
nft_data_release(&data, desc.type); return 0;
return err;
} }
static int nft_bitwise_fast_init(const struct nft_ctx *ctx, static int nft_bitwise_fast_init(const struct nft_ctx *ctx,
......
...@@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -73,20 +73,16 @@ static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_cmp_expr *priv = nft_expr_priv(expr); struct nft_cmp_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc; struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
};
int err; int err;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
tb[NFTA_CMP_DATA]);
if (err < 0) if (err < 0)
return err; return err;
if (desc.type != NFT_DATA_VALUE) {
err = -EINVAL;
nft_data_release(&priv->data, desc.type);
return err;
}
err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len); err = nft_parse_register_load(tb[NFTA_CMP_SREG], &priv->sreg, desc.len);
if (err < 0) if (err < 0)
return err; return err;
...@@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx, ...@@ -214,12 +210,14 @@ static int nft_cmp_fast_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); struct nft_cmp_fast_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc;
struct nft_data data; struct nft_data data;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
};
int err; int err;
err = nft_data_init(NULL, &data, sizeof(data), &desc, err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
tb[NFTA_CMP_DATA]);
if (err < 0) if (err < 0)
return err; return err;
...@@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx, ...@@ -313,11 +311,13 @@ static int nft_cmp16_fast_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr); struct nft_cmp16_fast_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc; struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data),
};
int err; int err;
err = nft_data_init(NULL, &priv->data, sizeof(priv->data), &desc, err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]);
tb[NFTA_CMP_DATA]);
if (err < 0) if (err < 0)
return err; return err;
...@@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = { ...@@ -380,8 +380,11 @@ const struct nft_expr_ops nft_cmp16_fast_ops = {
static const struct nft_expr_ops * static const struct nft_expr_ops *
nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
{ {
struct nft_data_desc desc;
struct nft_data data; struct nft_data data;
struct nft_data_desc desc = {
.type = NFT_DATA_VALUE,
.size = sizeof(data),
};
enum nft_cmp_ops op; enum nft_cmp_ops op;
u8 sreg; u8 sreg;
int err; int err;
...@@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) ...@@ -404,14 +407,10 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
err = nft_data_init(NULL, &data, sizeof(data), &desc, err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]);
tb[NFTA_CMP_DATA]);
if (err < 0) if (err < 0)
return ERR_PTR(err); return ERR_PTR(err);
if (desc.type != NFT_DATA_VALUE)
goto err1;
sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG]));
if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) { if (op == NFT_CMP_EQ || op == NFT_CMP_NEQ) {
...@@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) ...@@ -423,9 +422,6 @@ nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[])
return &nft_cmp16_fast_ops; return &nft_cmp16_fast_ops;
} }
return &nft_cmp_ops; return &nft_cmp_ops;
err1:
nft_data_release(&data, desc.type);
return ERR_PTR(-EINVAL);
} }
struct nft_expr_type nft_cmp_type __read_mostly = { struct nft_expr_type nft_cmp_type __read_mostly = {
......
...@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, ...@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
&regs->data[priv->sreg_key], NULL, &regs->data[priv->sreg_key], NULL,
&regs->data[priv->sreg_data], &regs->data[priv->sreg_data],
timeout, 0, GFP_ATOMIC); timeout, 0, GFP_ATOMIC);
if (elem == NULL) if (IS_ERR(elem))
goto err1; goto err1;
ext = nft_set_elem_ext(set, elem); ext = nft_set_elem_ext(set, elem);
......
...@@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { ...@@ -29,20 +29,36 @@ static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = {
[NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED },
}; };
static enum nft_data_types nft_reg_to_type(const struct nlattr *nla)
{
enum nft_data_types type;
u8 reg;
reg = ntohl(nla_get_be32(nla));
if (reg == NFT_REG_VERDICT)
type = NFT_DATA_VERDICT;
else
type = NFT_DATA_VALUE;
return type;
}
static int nft_immediate_init(const struct nft_ctx *ctx, static int nft_immediate_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_immediate_expr *priv = nft_expr_priv(expr); struct nft_immediate_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc; struct nft_data_desc desc = {
.size = sizeof(priv->data),
};
int err; int err;
if (tb[NFTA_IMMEDIATE_DREG] == NULL || if (tb[NFTA_IMMEDIATE_DREG] == NULL ||
tb[NFTA_IMMEDIATE_DATA] == NULL) tb[NFTA_IMMEDIATE_DATA] == NULL)
return -EINVAL; return -EINVAL;
err = nft_data_init(ctx, &priv->data, sizeof(priv->data), &desc, desc.type = nft_reg_to_type(tb[NFTA_IMMEDIATE_DREG]);
tb[NFTA_IMMEDIATE_DATA]); err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]);
if (err < 0) if (err < 0)
return err; return err;
......
...@@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr ...@@ -51,7 +51,14 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_range_expr *priv = nft_expr_priv(expr); struct nft_range_expr *priv = nft_expr_priv(expr);
struct nft_data_desc desc_from, desc_to; struct nft_data_desc desc_from = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data_from),
};
struct nft_data_desc desc_to = {
.type = NFT_DATA_VALUE,
.size = sizeof(priv->data_to),
};
int err; int err;
u32 op; u32 op;
...@@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr ...@@ -61,26 +68,16 @@ static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr
!tb[NFTA_RANGE_TO_DATA]) !tb[NFTA_RANGE_TO_DATA])
return -EINVAL; return -EINVAL;
err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from), err = nft_data_init(NULL, &priv->data_from, &desc_from,
&desc_from, tb[NFTA_RANGE_FROM_DATA]); tb[NFTA_RANGE_FROM_DATA]);
if (err < 0) if (err < 0)
return err; return err;
if (desc_from.type != NFT_DATA_VALUE) { err = nft_data_init(NULL, &priv->data_to, &desc_to,
err = -EINVAL; tb[NFTA_RANGE_TO_DATA]);
goto err1;
}
err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to),
&desc_to, tb[NFTA_RANGE_TO_DATA]);
if (err < 0) if (err < 0)
goto err1; goto err1;
if (desc_to.type != NFT_DATA_VALUE) {
err = -EINVAL;
goto err2;
}
if (desc_from.len != desc_to.len) { if (desc_from.len != desc_to.len) {
err = -EINVAL; err = -EINVAL;
goto err2; goto err2;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment