Skip to content

Commit 34aae2c

Browse files
committed
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>
1 parent b8c3bf0 commit 34aae2c

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
@@ -651,6 +651,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[];
651651
struct nft_set_ext_tmpl {
652652
u16 len;
653653
u8 offset[NFT_SET_EXT_NUM];
654+
u8 ext_len[NFT_SET_EXT_NUM];
654655
};
655656

656657
/**
@@ -680,7 +681,8 @@ static inline int nft_set_ext_add_length(struct nft_set_ext_tmpl *tmpl, u8 id,
680681
return -EINVAL;
681682

682683
tmpl->offset[id] = tmpl->len;
683-
tmpl->len += nft_set_ext_types[id].len + len;
684+
tmpl->ext_len[id] = nft_set_ext_types[id].len + len;
685+
tmpl->len += tmpl->ext_len[id];
684686

685687
return 0;
686688
}

net/netfilter/nf_tables_api.c

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

5470+
static int nft_set_ext_check(const struct nft_set_ext_tmpl *tmpl, u8 id, u32 len)
5471+
{
5472+
len += nft_set_ext_types[id].len;
5473+
if (len > tmpl->ext_len[id] ||
5474+
len > U8_MAX)
5475+
return -1;
5476+
5477+
return 0;
5478+
}
5479+
5480+
static int nft_set_ext_memcpy(const struct nft_set_ext_tmpl *tmpl, u8 id,
5481+
void *to, const void *from, u32 len)
5482+
{
5483+
if (nft_set_ext_check(tmpl, id, len) < 0)
5484+
return -1;
5485+
5486+
memcpy(to, from, len);
5487+
5488+
return 0;
5489+
}
5490+
54705491
void *nft_set_elem_init(const struct nft_set *set,
54715492
const struct nft_set_ext_tmpl *tmpl,
54725493
const u32 *key, const u32 *key_end,
@@ -5477,17 +5498,26 @@ void *nft_set_elem_init(const struct nft_set *set,
54775498

54785499
elem = kzalloc(set->ops->elemsize + tmpl->len, gfp);
54795500
if (elem == NULL)
5480-
return NULL;
5501+
return ERR_PTR(-ENOMEM);
54815502

54825503
ext = nft_set_elem_ext(set, elem);
54835504
nft_set_ext_init(ext, tmpl);
54845505

5485-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY))
5486-
memcpy(nft_set_ext_key(ext), key, set->klen);
5487-
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
5488-
memcpy(nft_set_ext_key_end(ext), key_end, set->klen);
5489-
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
5490-
memcpy(nft_set_ext_data(ext), data, set->dlen);
5506+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) &&
5507+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY,
5508+
nft_set_ext_key(ext), key, set->klen) < 0)
5509+
goto err_ext_check;
5510+
5511+
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) &&
5512+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END,
5513+
nft_set_ext_key_end(ext), key_end, set->klen) < 0)
5514+
goto err_ext_check;
5515+
5516+
if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) &&
5517+
nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA,
5518+
nft_set_ext_data(ext), data, set->dlen) < 0)
5519+
goto err_ext_check;
5520+
54915521
if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
54925522
*nft_set_ext_expiration(ext) = get_jiffies_64() + expiration;
54935523
if (expiration == 0)
@@ -5497,6 +5527,11 @@ void *nft_set_elem_init(const struct nft_set *set,
54975527
*nft_set_ext_timeout(ext) = timeout;
54985528

54995529
return elem;
5530+
5531+
err_ext_check:
5532+
kfree(elem);
5533+
5534+
return ERR_PTR(-EINVAL);
55005535
}
55015536

55025537
static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
@@ -5584,14 +5619,25 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
55845619
}
55855620

55865621
static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
5622+
const struct nft_set_ext_tmpl *tmpl,
55875623
const struct nft_set_ext *ext,
55885624
struct nft_expr *expr_array[],
55895625
u32 num_exprs)
55905626
{
55915627
struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext);
5628+
u32 len = sizeof(struct nft_set_elem_expr);
55925629
struct nft_expr *expr;
55935630
int i, err;
55945631

5632+
if (num_exprs == 0)
5633+
return 0;
5634+
5635+
for (i = 0; i < num_exprs; i++)
5636+
len += expr_array[i]->ops->size;
5637+
5638+
if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0)
5639+
return -EINVAL;
5640+
55955641
for (i = 0; i < num_exprs; i++) {
55965642
expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
55975643
err = nft_expr_clone(expr, expr_array[i]);
@@ -6054,17 +6100,23 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
60546100
}
60556101
}
60566102

6057-
err = -ENOMEM;
60586103
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
60596104
elem.key_end.val.data, elem.data.val.data,
60606105
timeout, expiration, GFP_KERNEL_ACCOUNT);
6061-
if (elem.priv == NULL)
6106+
if (IS_ERR(elem.priv)) {
6107+
err = PTR_ERR(elem.priv);
60626108
goto err_parse_data;
6109+
}
60636110

60646111
ext = nft_set_elem_ext(set, elem.priv);
60656112
if (flags)
60666113
*nft_set_ext_flags(ext) = flags;
6114+
60676115
if (ulen > 0) {
6116+
if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
6117+
err = -EINVAL;
6118+
goto err_elem_userdata;
6119+
}
60686120
udata = nft_set_ext_userdata(ext);
60696121
udata->len = ulen - 1;
60706122
nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
@@ -6073,14 +6125,14 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
60736125
*nft_set_ext_obj(ext) = obj;
60746126
obj->use++;
60756127
}
6076-
err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs);
6128+
err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
60776129
if (err < 0)
6078-
goto err_elem_expr;
6130+
goto err_elem_free;
60796131

60806132
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
60816133
if (trans == NULL) {
60826134
err = -ENOMEM;
6083-
goto err_elem_expr;
6135+
goto err_elem_free;
60846136
}
60856137

60866138
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
@@ -6126,10 +6178,10 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
61266178
nft_setelem_remove(ctx->net, set, &elem);
61276179
err_element_clash:
61286180
kfree(trans);
6129-
err_elem_expr:
6181+
err_elem_free:
61306182
if (obj)
61316183
obj->use--;
6132-
6184+
err_elem_userdata:
61336185
nf_tables_set_elem_destroy(ctx, set, elem.priv);
61346186
err_parse_data:
61356187
if (nla[NFTA_SET_ELEM_DATA] != NULL)
@@ -6311,8 +6363,10 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
63116363
elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data,
63126364
elem.key_end.val.data, NULL, 0, 0,
63136365
GFP_KERNEL_ACCOUNT);
6314-
if (elem.priv == NULL)
6366+
if (IS_ERR(elem.priv)) {
6367+
err = PTR_ERR(elem.priv);
63156368
goto fail_elem_key_end;
6369+
}
63166370

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