Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

+73 -17
+3 -1
include/net/netfilter/nf_tables.h
··· 651 651 struct nft_set_ext_tmpl { 652 652 u16 len; 653 653 u8 offset[NFT_SET_EXT_NUM]; 654 + u8 ext_len[NFT_SET_EXT_NUM]; 654 655 }; 655 656 656 657 /** ··· 681 680 return -EINVAL; 682 681 683 682 tmpl->offset[id] = tmpl->len; 684 - tmpl->len += nft_set_ext_types[id].len + len; 683 + tmpl->ext_len[id] = nft_set_ext_types[id].len + len; 684 + tmpl->len += tmpl->ext_len[id]; 685 685 686 686 return 0; 687 687 }
+69 -15
net/netfilter/nf_tables_api.c
··· 5467 5467 return ERR_PTR(err); 5468 5468 } 5469 5469 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 + 5470 5491 void *nft_set_elem_init(const struct nft_set *set, 5471 5492 const struct nft_set_ext_tmpl *tmpl, 5472 5493 const u32 *key, const u32 *key_end, ··· 5498 5477 5499 5478 elem = kzalloc(set->ops->elemsize + tmpl->len, gfp); 5500 5479 if (elem == NULL) 5501 - return NULL; 5480 + return ERR_PTR(-ENOMEM); 5502 5481 5503 5482 ext = nft_set_elem_ext(set, elem); 5504 5483 nft_set_ext_init(ext, tmpl); 5505 5484 5506 - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY)) 5507 - memcpy(nft_set_ext_key(ext), key, set->klen); 5508 - if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) 5509 - memcpy(nft_set_ext_key_end(ext), key_end, set->klen); 5510 - if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA)) 5511 - memcpy(nft_set_ext_data(ext), data, set->dlen); 5485 + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY) && 5486 + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY, 5487 + nft_set_ext_key(ext), key, set->klen) < 0) 5488 + goto err_ext_check; 5489 + 5490 + if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END) && 5491 + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_KEY_END, 5492 + nft_set_ext_key_end(ext), key_end, set->klen) < 0) 5493 + goto err_ext_check; 5494 + 5495 + if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA) && 5496 + nft_set_ext_memcpy(tmpl, NFT_SET_EXT_DATA, 5497 + nft_set_ext_data(ext), data, set->dlen) < 0) 5498 + goto err_ext_check; 5499 + 5512 5500 if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) { 5513 5501 *nft_set_ext_expiration(ext) = get_jiffies_64() + expiration; 5514 5502 if (expiration == 0) ··· 5527 5497 *nft_set_ext_timeout(ext) = timeout; 5528 5498 5529 5499 return elem; 5500 + 5501 + err_ext_check: 5502 + kfree(elem); 5503 + 5504 + return ERR_PTR(-EINVAL); 5530 5505 } 5531 5506 5532 5507 static void __nft_set_elem_expr_destroy(const struct nft_ctx *ctx, ··· 5619 5584 } 5620 5585 5621 5586 static int nft_set_elem_expr_setup(struct nft_ctx *ctx, 5587 + const struct nft_set_ext_tmpl *tmpl, 5622 5588 const struct nft_set_ext *ext, 5623 5589 struct nft_expr *expr_array[], 5624 5590 u32 num_exprs) 5625 5591 { 5626 5592 struct nft_set_elem_expr *elem_expr = nft_set_ext_expr(ext); 5593 + u32 len = sizeof(struct nft_set_elem_expr); 5627 5594 struct nft_expr *expr; 5628 5595 int i, err; 5596 + 5597 + if (num_exprs == 0) 5598 + return 0; 5599 + 5600 + for (i = 0; i < num_exprs; i++) 5601 + len += expr_array[i]->ops->size; 5602 + 5603 + if (nft_set_ext_check(tmpl, NFT_SET_EXT_EXPRESSIONS, len) < 0) 5604 + return -EINVAL; 5629 5605 5630 5606 for (i = 0; i < num_exprs; i++) { 5631 5607 expr = nft_setelem_expr_at(elem_expr, elem_expr->size); ··· 6100 6054 } 6101 6055 } 6102 6056 6103 - err = -ENOMEM; 6104 6057 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, 6105 6058 elem.key_end.val.data, elem.data.val.data, 6106 6059 timeout, expiration, GFP_KERNEL_ACCOUNT); 6107 - if (elem.priv == NULL) 6060 + if (IS_ERR(elem.priv)) { 6061 + err = PTR_ERR(elem.priv); 6108 6062 goto err_parse_data; 6063 + } 6109 6064 6110 6065 ext = nft_set_elem_ext(set, elem.priv); 6111 6066 if (flags) 6112 6067 *nft_set_ext_flags(ext) = flags; 6068 + 6113 6069 if (ulen > 0) { 6070 + if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) { 6071 + err = -EINVAL; 6072 + goto err_elem_userdata; 6073 + } 6114 6074 udata = nft_set_ext_userdata(ext); 6115 6075 udata->len = ulen - 1; 6116 6076 nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen); ··· 6125 6073 *nft_set_ext_obj(ext) = obj; 6126 6074 obj->use++; 6127 6075 } 6128 - err = nft_set_elem_expr_setup(ctx, ext, expr_array, num_exprs); 6076 + err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs); 6129 6077 if (err < 0) 6130 - goto err_elem_expr; 6078 + goto err_elem_free; 6131 6079 6132 6080 trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set); 6133 6081 if (trans == NULL) { 6134 6082 err = -ENOMEM; 6135 - goto err_elem_expr; 6083 + goto err_elem_free; 6136 6084 } 6137 6085 6138 6086 ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK; ··· 6178 6126 nft_setelem_remove(ctx->net, set, &elem); 6179 6127 err_element_clash: 6180 6128 kfree(trans); 6181 - err_elem_expr: 6129 + err_elem_free: 6182 6130 if (obj) 6183 6131 obj->use--; 6184 - 6132 + err_elem_userdata: 6185 6133 nf_tables_set_elem_destroy(ctx, set, elem.priv); 6186 6134 err_parse_data: 6187 6135 if (nla[NFTA_SET_ELEM_DATA] != NULL) ··· 6363 6311 elem.priv = nft_set_elem_init(set, &tmpl, elem.key.val.data, 6364 6312 elem.key_end.val.data, NULL, 0, 0, 6365 6313 GFP_KERNEL_ACCOUNT); 6366 - if (elem.priv == NULL) 6314 + if (IS_ERR(elem.priv)) { 6315 + err = PTR_ERR(elem.priv); 6367 6316 goto fail_elem_key_end; 6317 + } 6368 6318 6369 6319 ext = nft_set_elem_ext(set, elem.priv); 6370 6320 if (flags)
+1 -1
net/netfilter/nft_dynset.c
··· 60 60 &regs->data[priv->sreg_key], NULL, 61 61 &regs->data[priv->sreg_data], 62 62 timeout, 0, GFP_ATOMIC); 63 - if (elem == NULL) 63 + if (IS_ERR(elem)) 64 64 goto err1; 65 65 66 66 ext = nft_set_elem_ext(set, elem);