netfilter: ipset: Limit the maximal range of consecutive elements to add/delete
authorJozsef Kadlecsik <kadlec@netfilter.org>
Wed, 28 Jul 2021 15:01:15 +0000 (17:01 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 4 Aug 2021 08:41:03 +0000 (10:41 +0200)
The range size of consecutive elements were not limited. Thus one could
define a huge range which may result soft lockup errors due to the long
execution time. Now the range size is limited to 2^20 entries.

Reported-by: Brad Spengler <spender@grsecurity.net>
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/ipset/ip_set.h
net/netfilter/ipset/ip_set_hash_ip.c
net/netfilter/ipset/ip_set_hash_ipmark.c
net/netfilter/ipset/ip_set_hash_ipport.c
net/netfilter/ipset/ip_set_hash_ipportip.c
net/netfilter/ipset/ip_set_hash_ipportnet.c
net/netfilter/ipset/ip_set_hash_net.c
net/netfilter/ipset/ip_set_hash_netiface.c
net/netfilter/ipset/ip_set_hash_netnet.c
net/netfilter/ipset/ip_set_hash_netport.c
net/netfilter/ipset/ip_set_hash_netportnet.c

index 10279c4..ada1296 100644 (file)
@@ -196,6 +196,9 @@ struct ip_set_region {
        u32 elements;           /* Number of elements vs timeout */
 };
 
        u32 elements;           /* Number of elements vs timeout */
 };
 
+/* Max range where every element is added/deleted in one step */
+#define IPSET_MAX_RANGE                (1<<20)
+
 /* The max revision number supported by any set type + 1 */
 #define IPSET_REVISION_MAX     9
 
 /* The max revision number supported by any set type + 1 */
 #define IPSET_REVISION_MAX     9
 
index d1bef23..dd30c03 100644 (file)
@@ -132,8 +132,11 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
                if (ret)
                        return ret;
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
                if (ret)
                        return ret;
-               if (ip > ip_to)
+               if (ip > ip_to) {
+                       if (ip_to == 0)
+                               return -IPSET_ERR_HASH_ELEM;
                        swap(ip, ip_to);
                        swap(ip, ip_to);
+               }
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
@@ -144,6 +147,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
 
 
        hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
 
+       /* 64bit division is not allowed on 32bit */
+       if (((u64)ip_to - ip + 1) >> (32 - h->netmask) > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        if (retried) {
                ip = ntohl(h->next.ip);
                e.ip = htonl(ip);
        if (retried) {
                ip = ntohl(h->next.ip);
                e.ip = htonl(ip);
index 18346d1..153de34 100644 (file)
@@ -121,6 +121,8 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        e.mark = ntohl(nla_get_be32(tb[IPSET_ATTR_MARK]));
        e.mark &= h->markmask;
 
        e.mark = ntohl(nla_get_be32(tb[IPSET_ATTR_MARK]));
        e.mark &= h->markmask;
+       if (e.mark == 0 && e.ip == 0)
+               return -IPSET_ERR_HASH_ELEM;
 
        if (adt == IPSET_TEST ||
            !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) {
 
        if (adt == IPSET_TEST ||
            !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) {
@@ -133,8 +135,11 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
                if (ret)
                        return ret;
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
                if (ret)
                        return ret;
-               if (ip > ip_to)
+               if (ip > ip_to) {
+                       if (e.mark == 0 && ip_to == 0)
+                               return -IPSET_ERR_HASH_ELEM;
                        swap(ip, ip_to);
                        swap(ip, ip_to);
+               }
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
@@ -143,6 +148,9 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
                ip_set_mask_from_to(ip, ip_to, cidr);
        }
 
                ip_set_mask_from_to(ip, ip_to, cidr);
        }
 
+       if (((u64)ip_to - ip + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
index e1ca111..7303138 100644 (file)
@@ -173,6 +173,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
                        swap(port, port_to);
        }
 
                        swap(port, port_to);
        }
 
+       if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
index ab179e0..334fb1a 100644 (file)
@@ -180,6 +180,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
                        swap(port, port_to);
        }
 
                        swap(port, port_to);
        }
 
+       if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
        if (retried)
                ip = ntohl(h->next.ip);
        for (; ip <= ip_to; ip++) {
index 8f075b4..7df94f4 100644 (file)
@@ -253,6 +253,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                        swap(port, port_to);
        }
 
                        swap(port, port_to);
        }
 
+       if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        ip2_to = ip2_from;
        if (tb[IPSET_ATTR_IP2_TO]) {
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
        ip2_to = ip2_from;
        if (tb[IPSET_ATTR_IP2_TO]) {
                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
index c1a11f0..1422739 100644 (file)
@@ -140,7 +140,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);
        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;
+       u32 ip = 0, ip_to = 0, ipn, n = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -188,6 +188,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
                if (ip + UINT_MAX == ip_to)
                        return -IPSET_ERR_HASH_RANGE;
        }
                if (ip + UINT_MAX == ip_to)
                        return -IPSET_ERR_HASH_RANGE;
        }
+       ipn = ip;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr);
+               n++;
+       } while (ipn++ < ip_to);
+
+       if (n > IPSET_MAX_RANGE)
+               return -ERANGE;
+
        if (retried)
                ip = ntohl(h->next.ip);
        do {
        if (retried)
                ip = ntohl(h->next.ip);
        do {
index ddd51c2..9810f5b 100644 (file)
@@ -202,7 +202,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);
        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;
+       u32 ip = 0, ip_to = 0, ipn, n = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -256,6 +256,14 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        } else {
                ip_set_mask_from_to(ip, ip_to, e.cidr);
        }
        } else {
                ip_set_mask_from_to(ip, ip_to, e.cidr);
        }
+       ipn = ip;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr);
+               n++;
+       } while (ipn++ < ip_to);
+
+       if (n > IPSET_MAX_RANGE)
+               return -ERANGE;
 
        if (retried)
                ip = ntohl(h->next.ip);
 
        if (retried)
                ip = ntohl(h->next.ip);
index 6532f05..3d09eef 100644 (file)
@@ -168,7 +168,8 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 ip = 0, ip_to = 0;
        struct hash_netnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 ip = 0, ip_to = 0;
-       u32 ip2 = 0, ip2_from = 0, ip2_to = 0;
+       u32 ip2 = 0, ip2_from = 0, ip2_to = 0, ipn;
+       u64 n = 0, m = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -244,6 +245,19 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        } else {
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
        } else {
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
+       ipn = ip;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]);
+               n++;
+       } while (ipn++ < ip_to);
+       ipn = ip2_from;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]);
+               m++;
+       } while (ipn++ < ip2_to);
+
+       if (n*m > IPSET_MAX_RANGE)
+               return -ERANGE;
 
        if (retried) {
                ip = ntohl(h->next.ip[0]);
 
        if (retried) {
                ip = ntohl(h->next.ip[0]);
index ec1564a..09cf72e 100644 (file)
@@ -158,7 +158,8 @@ 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);
        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;
+       u32 port, port_to, p = 0, ip = 0, ip_to = 0, ipn;
+       u64 n = 0;
        bool with_ports = false;
        u8 cidr;
        int ret;
        bool with_ports = false;
        u8 cidr;
        int ret;
@@ -235,6 +236,14 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        } else {
                ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
        }
        } else {
                ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
        }
+       ipn = ip;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip_to, &cidr);
+               n++;
+       } while (ipn++ < ip_to);
+
+       if (n*(port_to - port + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
 
        if (retried) {
                ip = ntohl(h->next.ip);
 
        if (retried) {
                ip = ntohl(h->next.ip);
index 0e91d1e..19bcdb3 100644 (file)
@@ -182,7 +182,8 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        struct hash_netportnet4_elem e = { };
        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
        u32 ip = 0, ip_to = 0, p = 0, port, port_to;
        struct hash_netportnet4_elem e = { };
        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;
+       u32 ip2_from = 0, ip2_to = 0, ip2, ipn;
+       u64 n = 0, m = 0;
        bool with_ports = false;
        int ret;
 
        bool with_ports = false;
        int ret;
 
@@ -284,6 +285,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        } else {
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
        } else {
                ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
        }
+       ipn = ip;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]);
+               n++;
+       } while (ipn++ < ip_to);
+       ipn = ip2_from;
+       do {
+               ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]);
+               m++;
+       } while (ipn++ < ip2_to);
+
+       if (n*m*(port_to - port + 1) > IPSET_MAX_RANGE)
+               return -ERANGE;
 
        if (retried) {
                ip = ntohl(h->next.ip[0]);
 
        if (retried) {
                ip = ntohl(h->next.ip[0]);