From: Jozsef Kadlecsik <kad...@blackhole.kfki.hu>

Fix wraparound bug which could lead to memory exhaustion when adding an
x.x.x.x-255.255.255.255 range to any hash:*net* types.

Fixes Netfilter's bugzilla id #1212, reported by Thomas Schwark.

Fixes: 48596a8ddc46 ("netfilter: ipset: Fix adding an IPv4 range containing 
more than 2^31 addresses")
Signed-off-by: Jozsef Kadlecsik <kad...@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pa...@netfilter.org>
---
 net/netfilter/ipset/ip_set_hash_ipportnet.c  | 26 ++++++++++-----------
 net/netfilter/ipset/ip_set_hash_net.c        |  9 ++++---
 net/netfilter/ipset/ip_set_hash_netiface.c   |  9 ++++---
 net/netfilter/ipset/ip_set_hash_netnet.c     | 28 +++++++++++-----------
 net/netfilter/ipset/ip_set_hash_netport.c    | 19 ++++++++-------
 net/netfilter/ipset/ip_set_hash_netportnet.c | 35 ++++++++++++++--------------
 6 files changed, 63 insertions(+), 63 deletions(-)

diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c 
b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 0f164e986bf1..88b83d6d3084 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -168,7 +168,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr 
*tb[],
        struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 ip = 0, ip_to = 0, p = 0, port, port_to;
-       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+       u32 ip2_from = 0, ip2_to = 0, ip2;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -269,22 +269,21 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr 
*tb[],
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1);
        }
 
-       if (retried)
+       if (retried) {
                ip = ntohl(h->next.ip);
+               p = ntohs(h->next.port);
+               ip2 = ntohl(h->next.ip2);
+       } else {
+               p = port;
+               ip2 = ip2_from;
+       }
        for (; ip <= ip_to; ip++) {
                e.ip = htonl(ip);
-               p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
-                                                      : port;
                for (; p <= port_to; p++) {
                        e.port = htons(p);
-                       ip2 = retried &&
-                             ip == ntohl(h->next.ip) &&
-                             p == ntohs(h->next.port)
-                               ? ntohl(h->next.ip2) : ip2_from;
-                       while (ip2 <= ip2_to) {
+                       do {
                                e.ip2 = htonl(ip2);
-                               ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
-                                                               &cidr);
+                               ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr);
                                e.cidr = cidr - 1;
                                ret = adtfn(set, &e, &ext, &ext, flags);
 
@@ -292,9 +291,10 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr 
*tb[],
                                        return ret;
 
                                ret = 0;
-                               ip2 = ip2_last + 1;
-                       }
+                       } while (ip2++ < ip2_to);
+                       ip2 = ip2_from;
                }
+               p = port;
        }
        return ret;
 }
diff --git a/net/netfilter/ipset/ip_set_hash_net.c 
b/net/netfilter/ipset/ip_set_hash_net.c
index 1c67a1761e45..5449e23af13a 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -143,7 +143,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_net4_elem e = { .cidr = HOST_MASK };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
-       u32 ip = 0, ip_to = 0, last;
+       u32 ip = 0, ip_to = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -193,16 +193,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
        }
        if (retried)
                ip = ntohl(h->next.ip);
-       while (ip <= ip_to) {
+       do {
                e.ip = htonl(ip);
-               last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+               ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
                ret = adtfn(set, &e, &ext, &ext, flags);
                if (ret && !ip_set_eexist(ret, flags))
                        return ret;
 
                ret = 0;
-               ip = last + 1;
-       }
+       } while (ip++ < ip_to);
        return ret;
 }
 
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c 
b/net/netfilter/ipset/ip_set_hash_netiface.c
index d417074f1c1a..f5164c1efce2 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -200,7 +200,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
-       u32 ip = 0, ip_to = 0, last;
+       u32 ip = 0, ip_to = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -255,17 +255,16 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr 
*tb[],
 
        if (retried)
                ip = ntohl(h->next.ip);
-       while (ip <= ip_to) {
+       do {
                e.ip = htonl(ip);
-               last = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
+               ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
                ret = adtfn(set, &e, &ext, &ext, flags);
 
                if (ret && !ip_set_eexist(ret, flags))
                        return ret;
 
                ret = 0;
-               ip = last + 1;
-       }
+       } while (ip++ < ip_to);
        return ret;
 }
 
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c 
b/net/netfilter/ipset/ip_set_hash_netnet.c
index 7f9ae2e9645b..5a2b923bd81f 100644
--- a/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -169,8 +169,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
-       u32 ip = 0, ip_to = 0, last;
-       u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2;
+       u32 ip = 0, ip_to = 0;
+       u32 ip2 = 0, ip2_from = 0, ip2_to = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -247,27 +247,27 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
 
-       if (retried)
+       if (retried) {
                ip = ntohl(h->next.ip[0]);
+               ip2 = ntohl(h->next.ip[1]);
+       } else {
+               ip2 = ip2_from;
+       }
 
-       while (ip <= ip_to) {
+       do {
                e.ip[0] = htonl(ip);
-               last = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
-               ip2 = (retried &&
-                      ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1])
-                                                  : ip2_from;
-               while (ip2 <= ip2_to) {
+               ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
+               do {
                        e.ip[1] = htonl(ip2);
-                       last2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
+                       ip2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
                        ret = adtfn(set, &e, &ext, &ext, flags);
                        if (ret && !ip_set_eexist(ret, flags))
                                return ret;
 
                        ret = 0;
-                       ip2 = last2 + 1;
-               }
-               ip = last + 1;
-       }
+               } while (ip2++ < ip2_to);
+               ip2 = ip2_from;
+       } while (ip++ < ip_to);
        return ret;
 }
 
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c 
b/net/netfilter/ipset/ip_set_hash_netport.c
index e6ef382febe4..1a187be9ebc8 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -161,7 +161,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
-       u32 port, port_to, p = 0, ip = 0, ip_to = 0, last;
+       u32 port, port_to, p = 0, ip = 0, ip_to = 0;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -239,25 +239,26 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr 
*tb[],
                ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
        }
 
-       if (retried)
+       if (retried) {
                ip = ntohl(h->next.ip);
-       while (ip <= ip_to) {
+               p = ntohs(h->next.port);
+       } else {
+               p = port;
+       }
+       do {
                e.ip = htonl(ip);
-               last = ip_set_range_to_cidr(ip, ip_to, &cidr);
+               ip = ip_set_range_to_cidr(ip, ip_to, &cidr);
                e.cidr = cidr - 1;
-               p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
-                                                      : port;
                for (; p <= port_to; p++) {
                        e.port = htons(p);
                        ret = adtfn(set, &e, &ext, &ext, flags);
-
                        if (ret && !ip_set_eexist(ret, flags))
                                return ret;
 
                        ret = 0;
                }
-               ip = last + 1;
-       }
+               p = port;
+       } while (ip++ < ip_to);
        return ret;
 }
 
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c 
b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 8602f2595a1a..d391485a6acd 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -184,8 +184,8 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr 
*tb[],
        ipset_adtfn adtfn = set->variant->adt[adt];
        struct hash_netportnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
-       u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to;
-       u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2;
+       u32 ip = 0, ip_to = 0, p = 0, port, port_to;
+       u32 ip2_from = 0, ip2_to = 0, ip2;
        bool with_ports = false;
        int ret;
 
@@ -288,33 +288,34 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr 
*tb[],
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
 
-       if (retried)
+       if (retried) {
                ip = ntohl(h->next.ip[0]);
+               p = ntohs(h->next.port);
+               ip2 = ntohl(h->next.ip[1]);
+       } else {
+               p = port;
+               ip2 = ip2_from;
+       }
 
-       while (ip <= ip_to) {
+       do {
                e.ip[0] = htonl(ip);
-               ip_last = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
-               p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port)
-                                                         : port;
+               ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
                for (; p <= port_to; p++) {
                        e.port = htons(p);
-                       ip2 = (retried && ip == ntohl(h->next.ip[0]) &&
-                              p == ntohs(h->next.port)) ? ntohl(h->next.ip[1])
-                                                        : ip2_from;
-                       while (ip2 <= ip2_to) {
+                       do {
                                e.ip[1] = htonl(ip2);
-                               ip2_last = ip_set_range_to_cidr(ip2, ip2_to,
-                                                               &e.cidr[1]);
+                               ip2 = ip_set_range_to_cidr(ip2, ip2_to,
+                                                          &e.cidr[1]);
                                ret = adtfn(set, &e, &ext, &ext, flags);
                                if (ret && !ip_set_eexist(ret, flags))
                                        return ret;
 
                                ret = 0;
-                               ip2 = ip2_last + 1;
-                       }
+                       } while (ip2++ < ip2_to);
+                       ip2 = ip2_from;
                }
-               ip = ip_last + 1;
-       }
+               p = port;
+       } while (ip++ < ip_to);
        return ret;
 }
 
-- 
2.11.0

Reply via email to