summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net/bridge/netfilter/ebtables.c34
1 files changed, 24 insertions, 10 deletions
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index fcaefdd6200b..dfb58056a89a 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1428,16 +1428,12 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,
oldcounters = t->table->counters;
}
- if (copy_from_user(&tmp, user, sizeof(tmp))) {
- BUGPRINT("Cfu didn't work\n");
+ if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT;
- }
if (*len != sizeof(struct ebt_replace) + entries_size +
- (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0)) {
- BUGPRINT("Wrong size\n");
+ (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0))
return -EINVAL;
- }
if (tmp.nentries != nentries) {
BUGPRINT("Nentries wrong\n");
@@ -2213,8 +2209,12 @@ static int compat_do_replace(struct net *net, void __user *user,
void *entries_tmp;
ret = compat_copy_ebt_replace_from_user(&tmp, user, len);
- if (ret)
+ if (ret) {
+ /* try real handler in case userland supplied needed padding */
+ if (ret == -EINVAL && do_replace(net, user, len) == 0)
+ ret = 0;
return ret;
+ }
countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
@@ -2303,8 +2303,9 @@ static int compat_update_counters(struct net *net, void __user *user,
if (copy_from_user(&hlp, user, sizeof(hlp)))
return -EFAULT;
+ /* try real handler in case userland supplied needed padding */
if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter))
- return -EINVAL;
+ return update_counters(net, user, len);
return do_update_counters(net, hlp.name, compat_ptr(hlp.counters),
hlp.num_counters, user, len);
@@ -2341,9 +2342,10 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ /* try real handler in case userland supplied needed padding */
if ((cmd == EBT_SO_GET_INFO ||
cmd == EBT_SO_GET_INIT_INFO) && *len != sizeof(tmp))
- return -EINVAL;
+ return do_ebt_get_ctl(sk, cmd, user, len);
if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT;
@@ -2380,7 +2382,19 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
break;
case EBT_SO_GET_ENTRIES:
case EBT_SO_GET_INIT_ENTRIES:
- ret = compat_copy_everything_to_user(t, user, len, cmd);
+ /*
+ * try real handler first in case of userland-side padding.
+ * in case we are dealing with an 'ordinary' 32 bit binary
+ * without 64bit compatibility padding, this will fail right
+ * after copy_from_user when the *len argument is validated.
+ *
+ * the compat_ variant needs to do one pass over the kernel
+ * data set to adjust for size differences before it the check.
+ */
+ if (copy_everything_to_user(t, user, len, cmd) == 0)
+ ret = 0;
+ else
+ ret = compat_copy_everything_to_user(t, user, len, cmd);
break;
default:
ret = -EINVAL;