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

netfilter: nftables_offload: KASAN slab-out-of-bounds Read in nft_flow_rule_create

This patch fixes the issue due to:

BUG: KASAN: slab-out-of-bounds in nft_flow_rule_create+0x622/0x6a2
net/netfilter/nf_tables_offload.c:40
Read of size 8 at addr ffff888103910b58 by task syz-executor227/16244

The error happens when expr->ops is accessed early on before performing the boundary check and after nft_expr_next() moves the expr to go out-of-bounds.

This patch checks the boundary condition before expr->ops that fixes the slab-out-of-bounds Read issue.

Add nft_expr_more() and use it to fix this problem.

Signed-off-by: Saeed Mirzamohammadi <saeed.mirzamohammadi@oracle.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Saeed Mirzamohammadi and committed by
Pablo Neira Ayuso
31cc578a 64747d5e

+11 -5
+6
include/net/netfilter/nf_tables.h
··· 891 891 return (struct nft_expr *)&rule->data[rule->dlen]; 892 892 } 893 893 894 + static inline bool nft_expr_more(const struct nft_rule *rule, 895 + const struct nft_expr *expr) 896 + { 897 + return expr != nft_expr_last(rule) && expr->ops; 898 + } 899 + 894 900 static inline struct nft_userdata *nft_userdata(const struct nft_rule *rule) 895 901 { 896 902 return (void *)&rule->data[rule->dlen];
+3 -3
net/netfilter/nf_tables_api.c
··· 302 302 struct nft_expr *expr; 303 303 304 304 expr = nft_expr_first(rule); 305 - while (expr != nft_expr_last(rule) && expr->ops) { 305 + while (nft_expr_more(rule, expr)) { 306 306 if (expr->ops->activate) 307 307 expr->ops->activate(ctx, expr); 308 308 ··· 317 317 struct nft_expr *expr; 318 318 319 319 expr = nft_expr_first(rule); 320 - while (expr != nft_expr_last(rule) && expr->ops) { 320 + while (nft_expr_more(rule, expr)) { 321 321 if (expr->ops->deactivate) 322 322 expr->ops->deactivate(ctx, expr, phase); 323 323 ··· 3080 3080 * is called on error from nf_tables_newrule(). 3081 3081 */ 3082 3082 expr = nft_expr_first(rule); 3083 - while (expr != nft_expr_last(rule) && expr->ops) { 3083 + while (nft_expr_more(rule, expr)) { 3084 3084 next = nft_expr_next(expr); 3085 3085 nf_tables_expr_destroy(ctx, expr); 3086 3086 expr = next;
+2 -2
net/netfilter/nf_tables_offload.c
··· 37 37 struct nft_expr *expr; 38 38 39 39 expr = nft_expr_first(rule); 40 - while (expr->ops && expr != nft_expr_last(rule)) { 40 + while (nft_expr_more(rule, expr)) { 41 41 if (expr->ops->offload_flags & NFT_OFFLOAD_F_ACTION) 42 42 num_actions++; 43 43 ··· 61 61 ctx->net = net; 62 62 ctx->dep.type = NFT_OFFLOAD_DEP_UNSPEC; 63 63 64 - while (expr->ops && expr != nft_expr_last(rule)) { 64 + while (nft_expr_more(rule, expr)) { 65 65 if (!expr->ops->offload) { 66 66 err = -EOPNOTSUPP; 67 67 goto err_out;