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

netfilter: ebtables: fix table blob use-after-free

We are not allowed to return an error at this point.
Looking at the code it looks like ret is always 0 at this
point, but its not.

t = find_table_lock(net, repl->name, &ret, &ebt_mutex);

... this can return a valid table, with ret != 0.

This bug causes update of table->private with the new
blob, but then frees the blob right away in the caller.

Syzbot report:

BUG: KASAN: vmalloc-out-of-bounds in __ebt_unregister_table+0xc00/0xcd0 net/bridge/netfilter/ebtables.c:1168
Read of size 4 at addr ffffc90005425000 by task kworker/u4:4/74
Workqueue: netns cleanup_net
Call Trace:
kasan_report+0xbf/0x1f0 mm/kasan/report.c:517
__ebt_unregister_table+0xc00/0xcd0 net/bridge/netfilter/ebtables.c:1168
ebt_unregister_table+0x35/0x40 net/bridge/netfilter/ebtables.c:1372
ops_exit_list+0xb0/0x170 net/core/net_namespace.c:169
cleanup_net+0x4ee/0xb10 net/core/net_namespace.c:613
...

ip(6)tables appears to be ok (ret should be 0 at this point) but make
this more obvious.

Fixes: c58dd2dd443c ("netfilter: Can't fail and free after table replacement")
Reported-by: syzbot+f61594de72d6705aea03@syzkaller.appspotmail.com
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Florian Westphal and committed by
Pablo Neira Ayuso
e58a171d efb056e5

+3 -5
+1 -1
net/bridge/netfilter/ebtables.c
··· 1090 1090 1091 1091 audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, 1092 1092 AUDIT_XT_OP_REPLACE, GFP_KERNEL); 1093 - return ret; 1093 + return 0; 1094 1094 1095 1095 free_unlock: 1096 1096 mutex_unlock(&ebt_mutex);
+1 -2
net/ipv4/netfilter/ip_tables.c
··· 1045 1045 struct xt_counters *counters; 1046 1046 struct ipt_entry *iter; 1047 1047 1048 - ret = 0; 1049 1048 counters = xt_counters_alloc(num_counters); 1050 1049 if (!counters) { 1051 1050 ret = -ENOMEM; ··· 1090 1091 net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n"); 1091 1092 } 1092 1093 vfree(counters); 1093 - return ret; 1094 + return 0; 1094 1095 1095 1096 put_module: 1096 1097 module_put(t->me);
+1 -2
net/ipv6/netfilter/ip6_tables.c
··· 1062 1062 struct xt_counters *counters; 1063 1063 struct ip6t_entry *iter; 1064 1064 1065 - ret = 0; 1066 1065 counters = xt_counters_alloc(num_counters); 1067 1066 if (!counters) { 1068 1067 ret = -ENOMEM; ··· 1107 1108 net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n"); 1108 1109 } 1109 1110 vfree(counters); 1110 - return ret; 1111 + return 0; 1111 1112 1112 1113 put_module: 1113 1114 module_put(t->me);