Skip to content

Commit cde4746

Browse files
committed
netfilter: nf_tables: validate variable length element extension
jira VULN-430 cve-pre CVE-2023-4244 commit-author Pablo Neira Ayuso <pablo@netfilter.org> commit 34aae2c upstream-diff Used the cleanly applying 9.4 backport f74ccdb 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> (cherry picked from commit 34aae2c) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent ba24727 commit cde4746

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
@@ -5686,6 +5686,27 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
56865686
return ERR_PTR(err);
56875687
}
56885688

5689+
static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
5690+
{
5691+
len += nft_set_ext_types[id].len;
5692+
if (len > tmpl->ext_len[id] ||
5693+
len > U8_MAX)
5694+
return -1;
5695+
5696+
return 0;
5697+
}
5698+
5699+
static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
5700+
void *to, const void *from, u32 len)
5701+
{
5702+
if (nft_set_ext_check(tmpl, id, len) < 0)
5703+
return -1;
5704+
5705+
memcpy(to, from, len);
5706+
5707+
return 0;
5708+
}
5709+
56895710
void *nft_set_elem_init(const struct nft_set *set,
56905711
const struct nft_set_ext_tmpl *tmpl,
56915712
const u32 *key, const u32 *key_end,
@@ -5696,17 +5717,26 @@ void *nft_set_elem_init(const struct nft_set *set,
56965717

56975718
elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
56985719
if (elem == NULL)
5699-
return NULL;
5720+
return ERR_PTR(-ENOMEM);
57005721

57015722
ext = nft_set_elem_ext(set, elem);
57025723
nft_set_ext_init(ext, tmpl);
57035724

5704-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
5705-
memcpy(nft_set_ext_key(ext), key, set->klen);
5706-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
5707-
memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
5708-
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
5709-
memcpy(nft_set_ext_data(ext), data, set->dlen);
5725+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
5726+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
5727+
nft_set_ext_key(ext), key, set->klen) < 0)
5728+
goto err_ext_check;
5729+
5730+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
5731+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
5732+
nft_set_ext_key_end(ext), key_end, set->klen) < 0)
5733+
goto err_ext_check;
5734+
5735+
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
5736+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
5737+
nft_set_ext_data(ext), data, set->dlen) < 0)
5738+
goto err_ext_check;
5739+
57105740
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
57115741
*nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
57125742
if (expiration == 0)
@@ -5716,6 +5746,11 @@ void *nft_set_elem_init(const struct nft_set *set,
57165746
*nft_set_ext_timeout(ext) = timeout;
57175747

57185748
return elem;
5749+
5750+
err_ext_check:
5751+
kfree(elem);
5752+
5753+
return ERR_PTR(-EINVAL);
57195754
}
57205755

57215756
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
@@ -5803,14 +5838,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
58035838
}
58045839

58055840
static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
5841+
const struct nft_set_ext_tmpl *tmpl,
58065842
const struct nft_set_ext *ext,
58075843
struct nft_expr *expr_array[],
58085844
u32 num_exprs)
58095845
{
58105846
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
5847+
u32 len = sizeof(struct nft_set_elem_expr);
58115848
struct nft_expr *expr;
58125849
int i, err;
58135850

5851+
if (num_exprs == 0)
5852+
return 0;
5853+
5854+
for (i = 0; i < num_exprs; i++)
5855+
len += expr_array[i]->ops->size;
5856+
5857+
if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
5858+
return -EINVAL;
5859+
58145860
for (i = 0; i < num_exprs; i++) {
58155861
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
58165862
err = nft_expr_clone(expr, expr_array[i]);
@@ -6304,17 +6350,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
63046350
}
63056351
}
63066352

6307-
err = -ENOMEM;
63086353
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
63096354
elem.key_end.val.data, elem.data.val.data,
63106355
timeout, expiration, GFP_KERNEL);
6311-
if (elem.priv == NULL)
6356+
if (IS_ERR(elem.priv)) {
6357+
err = PTR_ERR(elem.priv);
63126358
goto err_parse_data;
6359+
}
63136360

63146361
ext = nft_set_elem_ext(set, elem.priv);
63156362
if (flags)
63166363
*nft_set_ext_flags(ext) = flags;
6364+
63176365
if (ulen > 0) {
6366+
if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
6367+
err = -EINVAL;
6368+
goto err_elem_userdata;
6369+
}
63186370
udata = nft_set_ext_userdata(ext);
63196371
udata->len = ulen - 1;
63206372
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
@@ -6323,14 +6375,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
63236375
*nft_set_ext_obj(ext) = obj;
63246376
obj->use++;
63256377
}
6326-
err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);
6378+
err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
63276379
if (err < 0)
6328-
goto err_elem_expr;
6380+
goto err_elem_free;
63296381

63306382
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
63316383
if (trans == NULL) {
63326384
err = -ENOMEM;
6333-
goto err_elem_expr;
6385+
goto err_elem_free;
63346386
}
63356387

63366388
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
@@ -6376,10 +6428,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
63766428
nft_setelem_remove(ctx->net, set, &elem);
63776429
err_element_clash:
63786430
kfree(trans);
6379-
err_elem_expr:
6431+
err_elem_free:
63806432
if (obj)
63816433
obj->use--;
6382-
6434+
err_elem_userdata:
63836435
nf_tables_set_elem_destroy(ctx, set, elem.priv);
63846436
err_parse_data:
63856437
if (nla[NFTA_SET_ELEM_DATA] != NULL)
@@ -6554,8 +6606,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
65546606
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
65556607
elem.key_end.val.data, NULL, 0, 0,
65566608
GFP_KERNEL);
6557-
if (elem.priv == NULL)
6609+
if (IS_ERR(elem.priv)) {
6610+
err = PTR_ERR(elem.priv);
65586611
goto fail_elem_key_end;
6612+
}
65596613

65606614
ext = nft_set_elem_ext(set, elem.priv);
65616615
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)