Skip to content

Commit

Permalink
netfilter: Can't fail and free after table replacement
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Thomas Graf <[email protected]>
Signed-off-by: Pablo Neira Ayuso <[email protected]>
  • Loading branch information
tgraf authored and ummakynes committed Apr 5, 2014
1 parent 2fec6bb commit c58dd2d
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 9 deletions.
5 changes: 2 additions & 3 deletions net/bridge/netfilter/ebtables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1044,10 +1044,9 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
if (repl->num_counters &&
copy_to_user(repl->counters, counterstmp,
repl->num_counters * sizeof(struct ebt_counter))) {
ret = -EFAULT;
/* Silent error, can't fail, new table is already in place */
net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");
}
else
ret = 0;

/* decrease module count and free resources */
EBT_ENTRY_ITERATE(table->entries, table->entries_size,
Expand Down
6 changes: 4 additions & 2 deletions net/ipv4/netfilter/arp_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1044,8 +1044,10 @@ static int __do_replace(struct net *net, const char *name,

xt_free_table_info(oldinfo);
if (copy_to_user(counters_ptr, counters,
sizeof(struct xt_counters) * num_counters) != 0)
ret = -EFAULT;
sizeof(struct xt_counters) * num_counters) != 0) {
/* Silent error, can't fail, new table is already in place */
net_warn_ratelimited("arptables: counters copy to user failed while replacing table\n");
}
vfree(counters);
xt_table_unlock(t);
return ret;
Expand Down
6 changes: 4 additions & 2 deletions net/ipv4/netfilter/ip_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1231,8 +1231,10 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,

xt_free_table_info(oldinfo);
if (copy_to_user(counters_ptr, counters,
sizeof(struct xt_counters) * num_counters) != 0)
ret = -EFAULT;
sizeof(struct xt_counters) * num_counters) != 0) {
/* Silent error, can't fail, new table is already in place */
net_warn_ratelimited("iptables: counters copy to user failed while replacing table\n");
}
vfree(counters);
xt_table_unlock(t);
return ret;
Expand Down
6 changes: 4 additions & 2 deletions net/ipv6/netfilter/ip6_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -1241,8 +1241,10 @@ __do_replace(struct net *net, const char *name, unsigned int valid_hooks,

xt_free_table_info(oldinfo);
if (copy_to_user(counters_ptr, counters,
sizeof(struct xt_counters) * num_counters) != 0)
ret = -EFAULT;
sizeof(struct xt_counters) * num_counters) != 0) {
/* Silent error, can't fail, new table is already in place */
net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n");
}
vfree(counters);
xt_table_unlock(t);
return ret;
Expand Down

0 comments on commit c58dd2d

Please sign in to comment.