[NETFILTER]: {ip,ip6,arp}_tables: return EAGAIN for invalid SO_GET_ENTRIES size
authorPatrick McHardy <kaber@trash.net>
Mon, 14 Apr 2008 09:15:45 +0000 (11:15 +0200)
committerPatrick McHardy <kaber@trash.net>
Mon, 14 Apr 2008 09:15:45 +0000 (11:15 +0200)
Rule dumping is performed in two steps: first userspace gets the
ruleset size using getsockopt(SO_GET_INFO) and allocates memory,
then it calls getsockopt(SO_GET_ENTRIES) to actually dump the
ruleset. When another process changes the ruleset in between the
sizes from the first getsockopt call doesn't match anymore and
the kernel aborts. Unfortunately it returns EAGAIN, as for multiple
other possible errors, so userspace can't distinguish this case
from real errors.

Return EAGAIN so userspace can retry the operation.

Fixes (with current iptables SVN version) netfilter bugzilla #104.

Signed-off-by: Patrick McHardy <kaber@trash.net>
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c

index d55f3b42eba57443108e3b0dc00e999ed391f678..03e83a65aec5eca5dab1b1c99401c3ac40985ed2 100644 (file)
@@ -937,7 +937,7 @@ static int get_entries(struct net *net, struct arpt_get_entries __user *uptr,
                else {
                        duprintf("get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                module_put(t->me);
                xt_table_unlock(t);
@@ -1621,7 +1621,7 @@ static int compat_get_entries(struct net *net,
                } else if (!ret) {
                        duprintf("compat_get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                xt_compat_flush_offsets(NF_ARP);
                module_put(t->me);
index aa124b50cb4a9f93715c87c71d25ad7df1505597..4e7c719445c264c9f55dc00fa1c6ce3d4fc36671 100644 (file)
@@ -1180,7 +1180,7 @@ get_entries(struct net *net, struct ipt_get_entries __user *uptr, int *len)
                else {
                        duprintf("get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                module_put(t->me);
                xt_table_unlock(t);
@@ -1939,7 +1939,7 @@ compat_get_entries(struct net *net, struct compat_ipt_get_entries __user *uptr,
                } else if (!ret) {
                        duprintf("compat_get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                xt_compat_flush_offsets(AF_INET);
                module_put(t->me);
index 782183f63366e136f11142270cdc347615759f8e..0b4557e03431fa05fae2037d4d0248950bcd3a2d 100644 (file)
@@ -1206,7 +1206,7 @@ get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
                else {
                        duprintf("get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                module_put(t->me);
                xt_table_unlock(t);
@@ -1966,7 +1966,7 @@ compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
                } else if (!ret) {
                        duprintf("compat_get_entries: I've got %u not %u!\n",
                                 private->size, get.size);
-                       ret = -EINVAL;
+                       ret = -EAGAIN;
                }
                xt_compat_flush_offsets(AF_INET6);
                module_put(t->me);