]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 2 Dec 2022 16:40:00 +0000 (17:40 +0100)
[ Upstream commit 5f7b51bf09baca8e4f80cbe879536842bafb5f31 ]

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>
Stable-dep-of: c7aa1a76d4a0 ("netfilter: ipset: regression in ip_set_hash_ip.c")
Signed-off-by: Sasha Levin <sashal@kernel.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 ab192720e2d6635f8e82bb6eeaaa96a5b0c47a26..53c9a17ecb3e3dc96aea06430b225b184242a6f7 100644 (file)
@@ -198,6 +198,9 @@ struct ip_set_region {
        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 core set type structure */
 struct ip_set_type {
        struct list_head list;
index 5d6d68eaf6a9c70a28a9252dbd781f013f51da69..361f4fd69bf42f64d749937a9ee24df5a42582fb 100644 (file)
@@ -131,8 +131,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;
-               if (ip > ip_to)
+               if (ip > ip_to) {
+                       if (ip_to == 0)
+                               return -IPSET_ERR_HASH_ELEM;
                        swap(ip, ip_to);
+               }
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
@@ -143,6 +146,10 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
 
        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);
index aba1df617d6e6e3f692329906d37b54a550f99ec..eefce34a34f08384ce96200c19fb52db67565bb9 100644 (file)
@@ -120,6 +120,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;
+       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])) {
@@ -132,8 +134,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;
-               if (ip > ip_to)
+               if (ip > ip_to) {
+                       if (e.mark == 0 && ip_to == 0)
+                               return -IPSET_ERR_HASH_ELEM;
                        swap(ip, ip_to);
+               }
        } else if (tb[IPSET_ATTR_CIDR]) {
                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 
@@ -142,6 +147,9 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
                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++) {
index 1ff228717e2989239f384f83f046a3ad21578dc1..4a54e9e8ae59f0560a34261dd02361a426d5de4a 100644 (file)
@@ -172,6 +172,9 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
                        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++) {
index fa88afd812fa6067fae9c7d89584d9324fb89102..09737de5ecc34cb0ae9af44c22e22d0723056ed2 100644 (file)
@@ -179,6 +179,9 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
                        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++) {
index eef6ecfcb40990da4012da2bf3dee53bd415980e..02685371a68289c09f752bfdfdc5bd0858841303 100644 (file)
@@ -252,6 +252,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
                        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);
index 136cf0781d3a4beff4bf7692d156f0f30e627c15..9d1beaacb9730c24e3d4ae493a15b45d2c881eea 100644 (file)
@@ -139,7 +139,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;
+       u32 ip = 0, ip_to = 0, ipn, n = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -187,6 +187,15 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
                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 {
index be5e95a0d8762fb3381650aba7831bdc69eae934..c3ada9c63fa3873e4badb459a59119dd1140fb3d 100644 (file)
@@ -201,7 +201,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;
+       u32 ip = 0, ip_to = 0, ipn, n = 0;
        int ret;
 
        if (tb[IPSET_ATTR_LINENO])
@@ -255,6 +255,14 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
        } 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);
index da4ef910b12d10c6ef74f3a1d9cdcb9a72ba8fa7..b1411bc91a404b82d12f51a1b3cf5cddc0ff1d07 100644 (file)
@@ -167,7 +167,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;
-       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])
@@ -243,6 +244,19 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        } 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]);
index 34448df80fb98a3134e3acb972eabb29774582af..d26d13528fe8bc9ff3ecdac02b553171f8d4e552 100644 (file)
@@ -157,7 +157,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);
-       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;
@@ -234,6 +235,14 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
        } 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);
index 934c1712cba853716694ce60846f1d700a4696de..6446f4fccc72983539b1d50b2f7e92b67f33102f 100644 (file)
@@ -181,7 +181,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;
-       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;
 
@@ -283,6 +284,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
        } 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]);