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

netfilter: Can't fail and free after table replacement

All xtables variants suffer from the defect that the copy_to_user()
to copy the counters to user memory may fail after the table has
already been exchanged and thus exposed. Return an error at this
point will result in freeing the already exposed table. Any
subsequent packet processing will result in a kernel panic.

We can't copy the counters before exposing the new tables as we
want provide the counter state after the old table has been
unhooked. Therefore convert this into a silent error.

Cc: Florian Westphal <fw@strlen.de>
Signed-off-by: Thomas Graf <tgraf@suug.ch>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Thomas Graf and committed by
Pablo Neira Ayuso
c58dd2dd 2fec6bb6

+14 -9
+2 -3
net/bridge/netfilter/ebtables.c
··· 1044 1044 if (repl->num_counters && 1045 1045 copy_to_user(repl->counters, counterstmp, 1046 1046 repl->num_counters * sizeof(struct ebt_counter))) { 1047 - ret = -EFAULT; 1047 + /* Silent error, can't fail, new table is already in place */ 1048 + net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n"); 1048 1049 } 1049 - else 1050 - ret = 0; 1051 1050 1052 1051 /* decrease module count and free resources */ 1053 1052 EBT_ENTRY_ITERATE(table->entries, table->entries_size,
+4 -2
net/ipv4/netfilter/arp_tables.c
··· 1044 1044 1045 1045 xt_free_table_info(oldinfo); 1046 1046 if (copy_to_user(counters_ptr, counters, 1047 - sizeof(struct xt_counters) * num_counters) != 0) 1048 - ret = -EFAULT; 1047 + sizeof(struct xt_counters) * num_counters) != 0) { 1048 + /* Silent error, can't fail, new table is already in place */ 1049 + net_warn_ratelimited("arptables: counters copy to user failed while replacing table\n"); 1050 + } 1049 1051 vfree(counters); 1050 1052 xt_table_unlock(t); 1051 1053 return ret;
+4 -2
net/ipv4/netfilter/ip_tables.c
··· 1231 1231 1232 1232 xt_free_table_info(oldinfo); 1233 1233 if (copy_to_user(counters_ptr, counters, 1234 - sizeof(struct xt_counters) * num_counters) != 0) 1235 - ret = -EFAULT; 1234 + sizeof(struct xt_counters) * num_counters) != 0) { 1235 + /* Silent error, can't fail, new table is already in place */ 1236 + net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n"); 1237 + } 1236 1238 vfree(counters); 1237 1239 xt_table_unlock(t); 1238 1240 return ret;
+4 -2
net/ipv6/netfilter/ip6_tables.c
··· 1241 1241 1242 1242 xt_free_table_info(oldinfo); 1243 1243 if (copy_to_user(counters_ptr, counters, 1244 - sizeof(struct xt_counters) * num_counters) != 0) 1245 - ret = -EFAULT; 1244 + sizeof(struct xt_counters) * num_counters) != 0) { 1245 + /* Silent error, can't fail, new table is already in place */ 1246 + net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n"); 1247 + } 1246 1248 vfree(counters); 1247 1249 xt_table_unlock(t); 1248 1250 return ret;