From e89c48042af340b40db465703467f3a6ac71e335 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 16 Nov 2015 14:39:09 -0500 Subject: [PATCH] Fix ipset netfilter issues (rhbz 1279189) --- kernel.spec | 6 + netfilter-ipset-Fix-extension-alignment.patch | 481 ++++++++++++++++++ ...ilter-ipset-Fix-hash-type-expiration.patch | 30 ++ ...Fix-hash-type-expire-release-empty-h.patch | 47 ++ 4 files changed, 564 insertions(+) create mode 100644 netfilter-ipset-Fix-extension-alignment.patch create mode 100644 netfilter-ipset-Fix-hash-type-expiration.patch create mode 100644 netfilter-ipset-Fix-hash-type-expire-release-empty-h.patch diff --git a/kernel.spec b/kernel.spec index 63f579ffc..00b006b17 100644 --- a/kernel.spec +++ b/kernel.spec @@ -613,6 +613,11 @@ Patch554: X.509-Fix-the-time-validation-ver-3.patch Patch555: net_43.mbox +#rhbz 1279189 +Patch556: netfilter-ipset-Fix-extension-alignment.patch +Patch557: netfilter-ipset-Fix-hash-type-expiration.patch +Patch558: netfilter-ipset-Fix-hash-type-expire-release-empty-h.patch + # END OF PATCH DEFINITIONS %endif @@ -2057,6 +2062,7 @@ fi # %changelog * Mon Nov 16 2015 Josh Boyer +- Fix ipset netfilter issues (rhbz 1279189) - Add queued 4.3 net stable fixes * Thu Nov 12 2015 Josh Boyer diff --git a/netfilter-ipset-Fix-extension-alignment.patch b/netfilter-ipset-Fix-extension-alignment.patch new file mode 100644 index 000000000..0a955e246 --- /dev/null +++ b/netfilter-ipset-Fix-extension-alignment.patch @@ -0,0 +1,481 @@ +From 55301931f78c0fdbb8f76dfdb3f914e9eef1f273 Mon Sep 17 00:00:00 2001 +From: Jozsef Kadlecsik +Date: Sat, 7 Nov 2015 11:21:47 +0100 +Subject: [PATCH 1/3] netfilter: ipset: Fix extension alignment + +The data extensions in ipset lacked the proper memory alignment and +thus could lead to kernel crash on several architectures. Therefore +the structures have been reorganized and alignment attributes added +where needed. The patch was tested on armv7h by Gerhard Wiesinger and +on x86_64, sparc64 by Jozsef Kadlecsik. + +Reported-by: Gerhard Wiesinger +Tested-by: Gerhard Wiesinger +Tested-by: Jozsef Kadlecsik +Signed-off-by: Jozsef Kadlecsik +--- + include/linux/netfilter/ipset/ip_set.h | 2 +- + net/netfilter/ipset/ip_set_bitmap_gen.h | 17 +++----- + net/netfilter/ipset/ip_set_bitmap_ip.c | 14 ++----- + net/netfilter/ipset/ip_set_bitmap_ipmac.c | 64 ++++++++++++++----------------- + net/netfilter/ipset/ip_set_bitmap_port.c | 18 ++++----- + net/netfilter/ipset/ip_set_core.c | 14 ++++--- + net/netfilter/ipset/ip_set_hash_gen.h | 11 ++++-- + net/netfilter/ipset/ip_set_list_set.c | 5 ++- + 8 files changed, 65 insertions(+), 80 deletions(-) + +diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h +index 48bb01edcf30..0e1f433cc4b7 100644 +--- a/include/linux/netfilter/ipset/ip_set.h ++++ b/include/linux/netfilter/ipset/ip_set.h +@@ -421,7 +421,7 @@ extern void ip_set_free(void *members); + extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); + extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); + extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], +- size_t len); ++ size_t len, size_t align); + extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], + struct ip_set_ext *ext); + +diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h +index d05e759ed0fa..b0bc475f641e 100644 +--- a/net/netfilter/ipset/ip_set_bitmap_gen.h ++++ b/net/netfilter/ipset/ip_set_bitmap_gen.h +@@ -33,7 +33,7 @@ + #define mtype_gc IPSET_TOKEN(MTYPE, _gc) + #define mtype MTYPE + +-#define get_ext(set, map, id) ((map)->extensions + (set)->dsize * (id)) ++#define get_ext(set, map, id) ((map)->extensions + ((set)->dsize * (id))) + + static void + mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) +@@ -67,12 +67,9 @@ mtype_destroy(struct ip_set *set) + del_timer_sync(&map->gc); + + ip_set_free(map->members); +- if (set->dsize) { +- if (set->extensions & IPSET_EXT_DESTROY) +- mtype_ext_cleanup(set); +- ip_set_free(map->extensions); +- } +- kfree(map); ++ if (set->dsize && set->extensions & IPSET_EXT_DESTROY) ++ mtype_ext_cleanup(set); ++ ip_set_free(map); + + set->data = NULL; + } +@@ -92,16 +89,14 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) + { + const struct mtype *map = set->data; + struct nlattr *nested; ++ size_t memsize = sizeof(*map) + map->memsize; + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + if (mtype_do_head(skb, map) || + nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || +- nla_put_net32(skb, IPSET_ATTR_MEMSIZE, +- htonl(sizeof(*map) + +- map->memsize + +- set->dsize * map->elements))) ++ nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) + goto nla_put_failure; + if (unlikely(ip_set_put_flags(skb, set))) + goto nla_put_failure; +diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c +index 64a564334418..4783efff0bde 100644 +--- a/net/netfilter/ipset/ip_set_bitmap_ip.c ++++ b/net/netfilter/ipset/ip_set_bitmap_ip.c +@@ -41,7 +41,6 @@ MODULE_ALIAS("ip_set_bitmap:ip"); + /* Type structure */ + struct bitmap_ip { + void *members; /* the set members */ +- void *extensions; /* data extensions */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ +@@ -49,6 +48,8 @@ struct bitmap_ip { + size_t memsize; /* members size */ + u8 netmask; /* subnet netmask */ + struct timer_list gc; /* garbage collection */ ++ unsigned char extensions[0] /* data extensions */ ++ __aligned(__alignof__(u64)); + }; + + /* ADT structure for generic function args */ +@@ -224,13 +225,6 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, + map->members = ip_set_alloc(map->memsize); + if (!map->members) + return false; +- if (set->dsize) { +- map->extensions = ip_set_alloc(set->dsize * elements); +- if (!map->extensions) { +- kfree(map->members); +- return false; +- } +- } + map->first_ip = first_ip; + map->last_ip = last_ip; + map->elements = elements; +@@ -316,13 +310,13 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + pr_debug("hosts %u, elements %llu\n", + hosts, (unsigned long long)elements); + +- map = kzalloc(sizeof(*map), GFP_KERNEL); ++ set->dsize = ip_set_elem_len(set, tb, 0, 0); ++ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); + if (!map) + return -ENOMEM; + + map->memsize = bitmap_bytes(0, elements - 1); + set->variant = &bitmap_ip; +- set->dsize = ip_set_elem_len(set, tb, 0); + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); +diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c +index 1430535118fb..29dde208381d 100644 +--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c ++++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c +@@ -47,24 +47,26 @@ enum { + /* Type structure */ + struct bitmap_ipmac { + void *members; /* the set members */ +- void *extensions; /* MAC + data extensions */ + u32 first_ip; /* host byte order, included in range */ + u32 last_ip; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ + size_t memsize; /* members size */ + struct timer_list gc; /* garbage collector */ ++ unsigned char extensions[0] /* MAC + data extensions */ ++ __aligned(__alignof__(u64)); + }; + + /* ADT structure for generic function args */ + struct bitmap_ipmac_adt_elem { ++ unsigned char ether[ETH_ALEN] __aligned(2); + u16 id; +- unsigned char *ether; ++ u16 add_mac; + }; + + struct bitmap_ipmac_elem { + unsigned char ether[ETH_ALEN]; + unsigned char filled; +-} __attribute__ ((aligned)); ++} __aligned(__alignof__(u64)); + + static inline u32 + ip_to_id(const struct bitmap_ipmac *m, u32 ip) +@@ -72,11 +74,11 @@ ip_to_id(const struct bitmap_ipmac *m, u32 ip) + return ip - m->first_ip; + } + +-static inline struct bitmap_ipmac_elem * +-get_elem(void *extensions, u16 id, size_t dsize) +-{ +- return (struct bitmap_ipmac_elem *)(extensions + id * dsize); +-} ++#define get_elem(extensions, id, dsize) \ ++ (struct bitmap_ipmac_elem *)(extensions + (id) * (dsize)) ++ ++#define get_const_elem(extensions, id, dsize) \ ++ (const struct bitmap_ipmac_elem *)(extensions + (id) * (dsize)) + + /* Common functions */ + +@@ -88,10 +90,9 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, + + if (!test_bit(e->id, map->members)) + return 0; +- elem = get_elem(map->extensions, e->id, dsize); +- if (elem->filled == MAC_FILLED) +- return !e->ether || +- ether_addr_equal(e->ether, elem->ether); ++ elem = get_const_elem(map->extensions, e->id, dsize); ++ if (e->add_mac && elem->filled == MAC_FILLED) ++ return ether_addr_equal(e->ether, elem->ether); + /* Trigger kernel to fill out the ethernet address */ + return -EAGAIN; + } +@@ -103,7 +104,7 @@ bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize) + + if (!test_bit(id, map->members)) + return 0; +- elem = get_elem(map->extensions, id, dsize); ++ elem = get_const_elem(map->extensions, id, dsize); + /* Timer not started for the incomplete elements */ + return elem->filled == MAC_FILLED; + } +@@ -133,7 +134,7 @@ bitmap_ipmac_add_timeout(unsigned long *timeout, + * and we can reuse it later when MAC is filled out, + * possibly by the kernel + */ +- if (e->ether) ++ if (e->add_mac) + ip_set_timeout_set(timeout, t); + else + *timeout = t; +@@ -150,7 +151,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, + elem = get_elem(map->extensions, e->id, dsize); + if (test_bit(e->id, map->members)) { + if (elem->filled == MAC_FILLED) { +- if (e->ether && ++ if (e->add_mac && + (flags & IPSET_FLAG_EXIST) && + !ether_addr_equal(e->ether, elem->ether)) { + /* memcpy isn't atomic */ +@@ -159,7 +160,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, + ether_addr_copy(elem->ether, e->ether); + } + return IPSET_ADD_FAILED; +- } else if (!e->ether) ++ } else if (!e->add_mac) + /* Already added without ethernet address */ + return IPSET_ADD_FAILED; + /* Fill the MAC address and trigger the timer activation */ +@@ -168,7 +169,7 @@ bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, + ether_addr_copy(elem->ether, e->ether); + elem->filled = MAC_FILLED; + return IPSET_ADD_START_STORED_TIMEOUT; +- } else if (e->ether) { ++ } else if (e->add_mac) { + /* We can store MAC too */ + ether_addr_copy(elem->ether, e->ether); + elem->filled = MAC_FILLED; +@@ -191,7 +192,7 @@ bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map, + u32 id, size_t dsize) + { + const struct bitmap_ipmac_elem *elem = +- get_elem(map->extensions, id, dsize); ++ get_const_elem(map->extensions, id, dsize); + + return nla_put_ipaddr4(skb, IPSET_ATTR_IP, + htonl(map->first_ip + id)) || +@@ -213,7 +214,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, + { + struct bitmap_ipmac *map = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; +- struct bitmap_ipmac_adt_elem e = { .id = 0 }; ++ struct bitmap_ipmac_adt_elem e = { .id = 0, .add_mac = 1 }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + u32 ip; + +@@ -231,7 +232,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, + return -EINVAL; + + e.id = ip_to_id(map, ip); +- e.ether = eth_hdr(skb)->h_source; ++ memcpy(e.ether, eth_hdr(skb)->h_source, ETH_ALEN); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); + } +@@ -265,11 +266,10 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], + return -IPSET_ERR_BITMAP_RANGE; + + e.id = ip_to_id(map, ip); +- if (tb[IPSET_ATTR_ETHER]) +- e.ether = nla_data(tb[IPSET_ATTR_ETHER]); +- else +- e.ether = NULL; +- ++ if (tb[IPSET_ATTR_ETHER]) { ++ memcpy(e.ether, nla_data(tb[IPSET_ATTR_ETHER]), ETH_ALEN); ++ e.add_mac = 1; ++ } + ret = adtfn(set, &e, &ext, &ext, flags); + + return ip_set_eexist(ret, flags) ? 0 : ret; +@@ -300,13 +300,6 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, + map->members = ip_set_alloc(map->memsize); + if (!map->members) + return false; +- if (set->dsize) { +- map->extensions = ip_set_alloc(set->dsize * elements); +- if (!map->extensions) { +- kfree(map->members); +- return false; +- } +- } + map->first_ip = first_ip; + map->last_ip = last_ip; + map->elements = elements; +@@ -361,14 +354,15 @@ bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + if (elements > IPSET_BITMAP_MAX_RANGE + 1) + return -IPSET_ERR_BITMAP_RANGE_SIZE; + +- map = kzalloc(sizeof(*map), GFP_KERNEL); ++ set->dsize = ip_set_elem_len(set, tb, ++ sizeof(struct bitmap_ipmac_elem), ++ __alignof__(struct bitmap_ipmac_elem)); ++ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); + if (!map) + return -ENOMEM; + + map->memsize = bitmap_bytes(0, elements - 1); + set->variant = &bitmap_ipmac; +- set->dsize = ip_set_elem_len(set, tb, +- sizeof(struct bitmap_ipmac_elem)); + if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { + kfree(map); + return -ENOMEM; +diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c +index 5338ccd5da46..7f0c733358a4 100644 +--- a/net/netfilter/ipset/ip_set_bitmap_port.c ++++ b/net/netfilter/ipset/ip_set_bitmap_port.c +@@ -35,12 +35,13 @@ MODULE_ALIAS("ip_set_bitmap:port"); + /* Type structure */ + struct bitmap_port { + void *members; /* the set members */ +- void *extensions; /* data extensions */ + u16 first_port; /* host byte order, included in range */ + u16 last_port; /* host byte order, included in range */ + u32 elements; /* number of max elements in the set */ + size_t memsize; /* members size */ + struct timer_list gc; /* garbage collection */ ++ unsigned char extensions[0] /* data extensions */ ++ __aligned(__alignof__(u64)); + }; + + /* ADT structure for generic function args */ +@@ -209,13 +210,6 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, + map->members = ip_set_alloc(map->memsize); + if (!map->members) + return false; +- if (set->dsize) { +- map->extensions = ip_set_alloc(set->dsize * map->elements); +- if (!map->extensions) { +- kfree(map->members); +- return false; +- } +- } + map->first_port = first_port; + map->last_port = last_port; + set->timeout = IPSET_NO_TIMEOUT; +@@ -232,6 +226,7 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + { + struct bitmap_port *map; + u16 first_port, last_port; ++ u32 elements; + + if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || +@@ -248,14 +243,15 @@ bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + last_port = tmp; + } + +- map = kzalloc(sizeof(*map), GFP_KERNEL); ++ elements = last_port - first_port + 1; ++ set->dsize = ip_set_elem_len(set, tb, 0, 0); ++ map = ip_set_alloc(sizeof(*map) + elements * set->dsize); + if (!map) + return -ENOMEM; + +- map->elements = last_port - first_port + 1; ++ map->elements = elements; + map->memsize = bitmap_bytes(0, map->elements); + set->variant = &bitmap_port; +- set->dsize = ip_set_elem_len(set, tb, 0); + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; +diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c +index 338b4047776f..cab4bc06cddd 100644 +--- a/net/netfilter/ipset/ip_set_core.c ++++ b/net/netfilter/ipset/ip_set_core.c +@@ -364,25 +364,27 @@ add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[]) + } + + size_t +-ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len) ++ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len, ++ size_t align) + { + enum ip_set_ext_id id; +- size_t offset = len; + u32 cadt_flags = 0; + + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_WITH_FORCEADD) + set->flags |= IPSET_CREATE_FLAG_FORCEADD; ++ if (!align) ++ align = 1; + for (id = 0; id < IPSET_EXT_ID_MAX; id++) { + if (!add_extension(id, cadt_flags, tb)) + continue; +- offset = ALIGN(offset, ip_set_extensions[id].align); +- set->offset[id] = offset; ++ len = ALIGN(len, ip_set_extensions[id].align); ++ set->offset[id] = len; + set->extensions |= ip_set_extensions[id].type; +- offset += ip_set_extensions[id].len; ++ len += ip_set_extensions[id].len; + } +- return offset; ++ return ALIGN(len, align); + } + EXPORT_SYMBOL_GPL(ip_set_elem_len); + +diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h +index 691b54fcaf2a..4ff22194ce55 100644 +--- a/net/netfilter/ipset/ip_set_hash_gen.h ++++ b/net/netfilter/ipset/ip_set_hash_gen.h +@@ -72,8 +72,9 @@ struct hbucket { + DECLARE_BITMAP(used, AHASH_MAX_TUNED); + u8 size; /* size of the array */ + u8 pos; /* position of the first free entry */ +- unsigned char value[0]; /* the array of the values */ +-} __attribute__ ((aligned)); ++ unsigned char value[0] /* the array of the values */ ++ __aligned(__alignof__(u64)); ++}; + + /* The hash table: the table size stored here in order to make resizing easy */ + struct htable { +@@ -1323,12 +1324,14 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, + #endif + set->variant = &IPSET_TOKEN(HTYPE, 4_variant); + set->dsize = ip_set_elem_len(set, tb, +- sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); ++ sizeof(struct IPSET_TOKEN(HTYPE, 4_elem)), ++ __alignof__(struct IPSET_TOKEN(HTYPE, 4_elem))); + #ifndef IP_SET_PROTO_UNDEF + } else { + set->variant = &IPSET_TOKEN(HTYPE, 6_variant); + set->dsize = ip_set_elem_len(set, tb, +- sizeof(struct IPSET_TOKEN(HTYPE, 6_elem))); ++ sizeof(struct IPSET_TOKEN(HTYPE, 6_elem)), ++ __alignof__(struct IPSET_TOKEN(HTYPE, 6_elem))); + } + #endif + if (tb[IPSET_ATTR_TIMEOUT]) { +diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c +index 5a30ce6e8c90..bbede95c9f68 100644 +--- a/net/netfilter/ipset/ip_set_list_set.c ++++ b/net/netfilter/ipset/ip_set_list_set.c +@@ -31,7 +31,7 @@ struct set_elem { + struct rcu_head rcu; + struct list_head list; + ip_set_id_t id; +-}; ++} __aligned(__alignof__(u64)); + + struct set_adt_elem { + ip_set_id_t id; +@@ -618,7 +618,8 @@ list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + size = IP_SET_LIST_MIN_SIZE; + + set->variant = &set_variant; +- set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem)); ++ set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem), ++ __alignof__(struct set_elem)); + if (!init_list_set(net, set, size)) + return -ENOMEM; + if (tb[IPSET_ATTR_TIMEOUT]) { +-- +2.4.3 + diff --git a/netfilter-ipset-Fix-hash-type-expiration.patch b/netfilter-ipset-Fix-hash-type-expiration.patch new file mode 100644 index 000000000..16ba4387f --- /dev/null +++ b/netfilter-ipset-Fix-hash-type-expiration.patch @@ -0,0 +1,30 @@ +From 7210b25e452780f0792e04dd9f84f3a02c582ab7 Mon Sep 17 00:00:00 2001 +From: Jozsef Kadlecsik +Date: Sat, 7 Nov 2015 11:23:34 +0100 +Subject: [PATCH 2/3] netfilter: ipset: Fix hash:* type expiration + +Incorrect index was used when the data blob was shrinked at expiration, +which could lead to falsely expired entries and memory leak when +the comment extension was used too. + +Signed-off-by: Jozsef Kadlecsik +--- + net/netfilter/ipset/ip_set_hash_gen.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h +index 4ff22194ce55..fa4f6374bb73 100644 +--- a/net/netfilter/ipset/ip_set_hash_gen.h ++++ b/net/netfilter/ipset/ip_set_hash_gen.h +@@ -523,7 +523,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) + continue; + data = ahash_data(n, j, dsize); + memcpy(tmp->value + d * dsize, data, dsize); +- set_bit(j, tmp->used); ++ set_bit(d, tmp->used); + d++; + } + tmp->pos = d; +-- +2.4.3 + diff --git a/netfilter-ipset-Fix-hash-type-expire-release-empty-h.patch b/netfilter-ipset-Fix-hash-type-expire-release-empty-h.patch new file mode 100644 index 000000000..1f0d86373 --- /dev/null +++ b/netfilter-ipset-Fix-hash-type-expire-release-empty-h.patch @@ -0,0 +1,47 @@ +From 03fdcf282c8fe212efae0d1229fb8594ffe60b17 Mon Sep 17 00:00:00 2001 +From: Jozsef Kadlecsik +Date: Sat, 7 Nov 2015 11:24:51 +0100 +Subject: [PATCH 3/3] netfilter: ipset: Fix hash type expire: release empty + hash bucket block + +When all entries are expired/all slots are empty, release the bucket. + +Signed-off-by: Jozsef Kadlecsik +--- + net/netfilter/ipset/ip_set_hash_gen.h | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h +index fa4f6374bb73..e5336ab36d67 100644 +--- a/net/netfilter/ipset/ip_set_hash_gen.h ++++ b/net/netfilter/ipset/ip_set_hash_gen.h +@@ -476,7 +476,7 @@ static void + mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) + { + struct htable *t; +- struct hbucket *n; ++ struct hbucket *n, *tmp; + struct mtype_elem *data; + u32 i, j, d; + #ifdef IP_SET_HASH_WITH_NETS +@@ -511,9 +511,14 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) + } + } + if (d >= AHASH_INIT_SIZE) { +- struct hbucket *tmp = kzalloc(sizeof(*tmp) + +- (n->size - AHASH_INIT_SIZE) * dsize, +- GFP_ATOMIC); ++ if (d >= n->size) { ++ rcu_assign_pointer(hbucket(t, i), NULL); ++ kfree_rcu(n, rcu); ++ continue; ++ } ++ tmp = kzalloc(sizeof(*tmp) + ++ (n->size - AHASH_INIT_SIZE) * dsize, ++ GFP_ATOMIC); + if (!tmp) + /* Still try to delete expired elements */ + continue; +-- +2.4.3 +