netfilter: ipset: fix hash size checking in kernel
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Mon, 14 May 2012 01:47:01 +0000 (01:47 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 16 May 2012 19:38:49 +0000 (15:38 -0400)
The hash size must fit both into u32 (jhash) and the max value of
size_t. The missing checking could lead to kernel crash, bug reported
by Seblu.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netfilter/ipset/ip_set_ahash.h
net/netfilter/ipset/ip_set_hash_ip.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_netport.c

index 05a5d72680bed904c23687a69e43a7759bc61848..230a290e1973c63f4411154e581692669e37ee08 100644 (file)
@@ -99,6 +99,22 @@ struct ip_set_hash {
 #endif
 };
 
+static size_t
+htable_size(u8 hbits)
+{
+       size_t hsize;
+
+       /* We must fit both into u32 in jhash and size_t */
+       if (hbits > 31)
+               return 0;
+       hsize = jhash_size(hbits);
+       if ((((size_t)-1) - sizeof(struct htable))/sizeof(struct hbucket)
+           < hsize)
+               return 0;
+
+       return hsize * sizeof(struct hbucket) + sizeof(struct htable);
+}
+
 /* Compute htable_bits from the user input parameter hashsize */
 static u8
 htable_bits(u32 hashsize)
index 5139dea6019e44ba24ee01d9c4f65beb3eddfe61..828ce46cb34b9ad6eb90a8363461e6f1e24c9344 100644 (file)
@@ -364,6 +364,7 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
 {
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 netmask, hbits;
+       size_t hsize;
        struct ip_set_hash *h;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
@@ -405,9 +406,12 @@ hash_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index 9c27e249c1713bb14d9d03c730bd2a5a125c6177..e8dbb498af8f465fe38866b4ae0cf9038c1bfbaa 100644 (file)
@@ -449,6 +449,7 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -476,9 +477,12 @@ hash_ipport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index 9134057c07284cf41b1e2d119415e8c043a0fc9a..52f79d8ef741cfb432ae2a66cb7913b82e7c94a4 100644 (file)
@@ -467,6 +467,7 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -494,9 +495,12 @@ hash_ipportip_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index 5d05e69698626570ca8247732d4bb8f8af632674..97583f5af7457e75763fc9aed7f85a38d6cb7306 100644 (file)
@@ -616,6 +616,7 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -645,9 +646,12 @@ hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index 7c3d945517cfa55c62faeccb24d6bce29c958faa..1721cdecc9f9eee4d91c75314a5e325327972322 100644 (file)
@@ -460,6 +460,7 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        struct ip_set_hash *h;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -489,9 +490,12 @@ hash_net_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index f24037ff432201015e81731f61133d45079128d6..33bafc97ca6d0e800426e85bb8b558b9516f2d30 100644 (file)
@@ -722,6 +722,7 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -752,9 +753,12 @@ hash_netiface_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->ahash_max = AHASH_MAX_SIZE;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;
index ce2e77100b64ecb521db1cf45c0babd4902a374f..3a5e198641d6a9d9bb3c1ce6a36c0f6fac800fcf 100644 (file)
@@ -572,6 +572,7 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        struct ip_set_hash *h;
        u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM;
        u8 hbits;
+       size_t hsize;
 
        if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6))
                return -IPSET_ERR_INVALID_FAMILY;
@@ -601,9 +602,12 @@ hash_netport_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
        h->timeout = IPSET_NO_TIMEOUT;
 
        hbits = htable_bits(hashsize);
-       h->table = ip_set_alloc(
-                       sizeof(struct htable)
-                       + jhash_size(hbits) * sizeof(struct hbucket));
+       hsize = htable_size(hbits);
+       if (hsize == 0) {
+               kfree(h);
+               return -ENOMEM;
+       }
+       h->table = ip_set_alloc(hsize);
        if (!h->table) {
                kfree(h);
                return -ENOMEM;