Skip to content

Commit f74ccdb

Browse files
committed
netfilter: nf_tables: validate variable length element extension
JIRA: https://issues.redhat.com/browse/RHEL-1720 JIRA: https://issues.redhat.com/browse/RHEL-1721 Upstream Status: commit 34aae2c Conflicts: net/netfilter/nf_tables_api.c Context: Upstream switched tp KERNEL_ACCOUNT in 33758c8 ("memcg: enable accounting for nft objects"). commit 34aae2c Author: Pablo Neira Ayuso <pablo@netfilter.org> Date: Tue Aug 9 11:25:43 2022 +0200 netfilter: nf_tables: validate variable length element extension Update template to validate variable length extensions. This patch adds a new .ext_len[id] field to the template to store the expected extension length. This is used to sanity check the initialization of the variable length extension. Use PTR_ERR() in nft_set_elem_init() to report errors since, after this update, there are two reason why this might fail, either because of ENOMEM or insufficient room in the extension field (EINVAL). Kernels up until 7e6bc1f ("netfilter: nf_tables: stricter validation of element data") allowed to copy more data to the extension than was allocated. This ext_len field allows to validate if the destination has the correct size as additional check. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Florian Westphal <fwestpha@redhat.com>
1 parent 5d9bf13 commit f74ccdb

File tree

3 files changed

+73
-17
lines changed

3 files changed

+73
-17
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[];
641641
struct nft_set_ext_tmpl {
642642
u16 len;
643643
u8 offset[NFT_SET_EXT_NUM];
644+
u8 ext_len[NFT_SET_EXT_NUM];
644645
};
645646

646647
/**
@@ -670,7 +671,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id,
670671
return -EINVAL;
671672

672673
tmpl->offset[id] = tmpl->len;
673-
tmpl->len += nft_set_ext_types[id].len + len;
674+
tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
675+
tmpl->len += tmpl->ext_len[id];
674676

675677
return 0;
676678
}

net/netfilter/nf_tables_api.c

Lines changed: 69 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5659,6 +5659,27 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
56595659
return ERR_PTR(err);
56605660
}
56615661

5662+
static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
5663+
{
5664+
len += nft_set_ext_types[id].len;
5665+
if (len > tmpl->ext_len[id] ||
5666+
len > U8_MAX)
5667+
return -1;
5668+
5669+
return 0;
5670+
}
5671+
5672+
static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
5673+
void *to, const void *from, u32 len)
5674+
{
5675+
if (nft_set_ext_check(tmpl, id, len) < 0)
5676+
return -1;
5677+
5678+
memcpy(to, from, len);
5679+
5680+
return 0;
5681+
}
5682+
56625683
void *nft_set_elem_init(const struct nft_set *set,
56635684
const struct nft_set_ext_tmpl *tmpl,
56645685
const u32 *key, const u32 *key_end,
@@ -5669,17 +5690,26 @@ void *nft_set_elem_init(const struct nft_set *set,
56695690

56705691
elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
56715692
if (elem == NULL)
5672-
return NULL;
5693+
return ERR_PTR(-ENOMEM);
56735694

56745695
ext = nft_set_elem_ext(set, elem);
56755696
nft_set_ext_init(ext, tmpl);
56765697

5677-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
5678-
memcpy(nft_set_ext_key(ext), key, set->klen);
5679-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
5680-
memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
5681-
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
5682-
memcpy(nft_set_ext_data(ext), data, set->dlen);
5698+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
5699+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
5700+
nft_set_ext_key(ext), key, set->klen) < 0)
5701+
goto err_ext_check;
5702+
5703+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
5704+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
5705+
nft_set_ext_key_end(ext), key_end, set->klen) < 0)
5706+
goto err_ext_check;
5707+
5708+
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
5709+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
5710+
nft_set_ext_data(ext), data, set->dlen) < 0)
5711+
goto err_ext_check;
5712+
56835713
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
56845714
*nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
56855715
if (expiration == 0)
@@ -5689,6 +5719,11 @@ void *nft_set_elem_init(const struct nft_set *set,
56895719
*nft_set_ext_timeout(ext) = timeout;
56905720

56915721
return elem;
5722+
5723+
err_ext_check:
5724+
kfree(elem);
5725+
5726+
return ERR_PTR(-EINVAL);
56925727
}
56935728

56945729
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
@@ -5776,14 +5811,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
57765811
}
57775812

57785813
static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
5814+
const struct nft_set_ext_tmpl *tmpl,
57795815
const struct nft_set_ext *ext,
57805816
struct nft_expr *expr_array[],
57815817
u32 num_exprs)
57825818
{
57835819
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
5820+
u32 len = sizeof(struct nft_set_elem_expr);
57845821
struct nft_expr *expr;
57855822
int i, err;
57865823

5824+
if (num_exprs == 0)
5825+
return 0;
5826+
5827+
for (i = 0; i < num_exprs; i++)
5828+
len += expr_array[i]->ops->size;
5829+
5830+
if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
5831+
return -EINVAL;
5832+
57875833
for (i = 0; i < num_exprs; i++) {
57885834
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
57895835
err = nft_expr_clone(expr, expr_array[i]);
@@ -6277,17 +6323,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
62776323
}
62786324
}
62796325

6280-
err = -ENOMEM;
62816326
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
62826327
elem.key_end.val.data, elem.data.val.data,
62836328
timeout, expiration, GFP_KERNEL);
6284-
if (elem.priv == NULL)
6329+
if (IS_ERR(elem.priv)) {
6330+
err = PTR_ERR(elem.priv);
62856331
goto err_parse_data;
6332+
}
62866333

62876334
ext = nft_set_elem_ext(set, elem.priv);
62886335
if (flags)
62896336
*nft_set_ext_flags(ext) = flags;
6337+
62906338
if (ulen > 0) {
6339+
if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
6340+
err = -EINVAL;
6341+
goto err_elem_userdata;
6342+
}
62916343
udata = nft_set_ext_userdata(ext);
62926344
udata->len = ulen - 1;
62936345
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
@@ -6296,14 +6348,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
62966348
*nft_set_ext_obj(ext) = obj;
62976349
obj->use++;
62986350
}
6299-
err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);
6351+
err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
63006352
if (err < 0)
6301-
goto err_elem_expr;
6353+
goto err_elem_free;
63026354

63036355
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
63046356
if (trans == NULL) {
63056357
err = -ENOMEM;
6306-
goto err_elem_expr;
6358+
goto err_elem_free;
63076359
}
63086360

63096361
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
@@ -6349,10 +6401,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
63496401
nft_setelem_remove(ctx->net, set, &elem);
63506402
err_element_clash:
63516403
kfree(trans);
6352-
err_elem_expr:
6404+
err_elem_free:
63536405
if (obj)
63546406
obj->use--;
6355-
6407+
err_elem_userdata:
63566408
nf_tables_set_elem_destroy(ctx, set, elem.priv);
63576409
err_parse_data:
63586410
if (nla[NFTA_SET_ELEM_DATA] != NULL)
@@ -6527,8 +6579,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
65276579
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
65286580
elem.key_end.val.data, NULL, 0, 0,
65296581
GFP_KERNEL);
6530-
if (elem.priv == NULL)
6582+
if (IS_ERR(elem.priv)) {
6583+
err = PTR_ERR(elem.priv);
65316584
goto fail_elem_key_end;
6585+
}
65326586

65336587
ext = nft_set_elem_ext(set, elem.priv);
65346588
if (flags)

net/netfilter/nft_dynset.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr,
6060
&regs->data[priv->sreg_key], NULL,
6161
&regs->data[priv->sreg_data],
6262
timeout, 0, GFP_ATOMIC);
63-
if (elem == NULL)
63+
if (IS_ERR(elem))
6464
goto err1;
6565

6666
ext = nft_set_elem_ext(set, elem);

0 commit comments

Comments
 (0)