History

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 7e6bc1f6cabc ("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.

Pablo Neira Ayuso committed 4 years ago

Commit 34aae2c1 parent b8c3bf03 files · +73 −17Browse files

Unified · Split

3 files
  1. include/net/netfilter/nf_tables.h +3 −1
  2. net/netfilter/nf_tables_api.c +69 −15
  3. net/netfilter/nft_dynset.c +1 −1
include/net/netfilter/nf_tables.h+3−1
@@ -651,6 +651,7 @@ extern const struct nft_set_ext_type nft_set_ext_types[]; · 650 unmodified lines
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, · 23 unmodified lines
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+69−15
@@ -5467,6 +5467,27 @@ err_set_elem_expr: · 5466 unmodified lines
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, · 4 unmodified lines
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, · 3 unmodified lines
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 @@ err_expr: · 81 unmodified lines
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, · 456 unmodified lines
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, · 2 unmodified lines
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 @@ err_set_full: · 39 unmodified lines
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, · 175 unmodified lines
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+1−1
@@ -60,7 +60,7 @@ static void *nft_dynset_new(struct nft_set *set, const struct nft_expr *expr, · 59 unmodified lines
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);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t