--- /dev/null
+From 32c72165dbd0e246e69d16a3ad348a4851afd415 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Kadlecsik=20J=C3=B3zsef?= <kadlec@blackhole.kfki.hu>
+Date: Sun, 19 Jan 2020 22:06:49 +0100
+Subject: netfilter: ipset: use bitmap infrastructure completely
+
+From: Kadlecsik József <kadlec@blackhole.kfki.hu>
+
+commit 32c72165dbd0e246e69d16a3ad348a4851afd415 upstream.
+
+The bitmap allocation did not use full unsigned long sizes
+when calculating the required size and that was triggered by KASAN
+as slab-out-of-bounds read in several places. The patch fixes all
+of them.
+
+Reported-by: syzbot+fabca5cbf5e54f3fe2de@syzkaller.appspotmail.com
+Reported-by: syzbot+827ced406c9a1d9570ed@syzkaller.appspotmail.com
+Reported-by: syzbot+190d63957b22ef673ea5@syzkaller.appspotmail.com
+Reported-by: syzbot+dfccdb2bdb4a12ad425e@syzkaller.appspotmail.com
+Reported-by: syzbot+df0d0f5895ef1f41a65b@syzkaller.appspotmail.com
+Reported-by: syzbot+b08bd19bb37513357fd4@syzkaller.appspotmail.com
+Reported-by: syzbot+53cdd0ec0bbabd53370a@syzkaller.appspotmail.com
+Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/ipset/ip_set.h | 7 -------
+ net/netfilter/ipset/ip_set_bitmap_gen.h | 2 +-
+ net/netfilter/ipset/ip_set_bitmap_ip.c | 6 +++---
+ net/netfilter/ipset/ip_set_bitmap_ipmac.c | 6 +++---
+ net/netfilter/ipset/ip_set_bitmap_port.c | 6 +++---
+ 5 files changed, 10 insertions(+), 17 deletions(-)
+
+--- a/include/linux/netfilter/ipset/ip_set.h
++++ b/include/linux/netfilter/ipset/ip_set.h
+@@ -445,13 +445,6 @@ ip6addrptr(const struct sk_buff *skb, bo
+ sizeof(*addr));
+ }
+
+-/* Calculate the bytes required to store the inclusive range of a-b */
+-static inline int
+-bitmap_bytes(u32 a, u32 b)
+-{
+- return 4 * ((((b - a + 8) / 8) + 3) / 4);
+-}
+-
+ /* How often should the gc be run by default */
+ #define IPSET_GC_TIME (3 * 60)
+
+--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
++++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
+@@ -75,7 +75,7 @@ mtype_flush(struct ip_set *set)
+
+ if (set->extensions & IPSET_EXT_DESTROY)
+ mtype_ext_cleanup(set);
+- memset(map->members, 0, map->memsize);
++ bitmap_zero(map->members, map->elements);
+ set->elements = 0;
+ set->ext_size = 0;
+ }
+--- a/net/netfilter/ipset/ip_set_bitmap_ip.c
++++ b/net/netfilter/ipset/ip_set_bitmap_ip.c
+@@ -37,7 +37,7 @@ MODULE_ALIAS("ip_set_bitmap:ip");
+
+ /* Type structure */
+ struct bitmap_ip {
+- void *members; /* the set members */
++ unsigned long *members; /* the set members */
+ 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 */
+@@ -220,7 +220,7 @@ init_map_ip(struct ip_set *set, struct b
+ u32 first_ip, u32 last_ip,
+ u32 elements, u32 hosts, u8 netmask)
+ {
+- map->members = ip_set_alloc(map->memsize);
++ map->members = bitmap_zalloc(elements, GFP_KERNEL | __GFP_NOWARN);
+ if (!map->members)
+ return false;
+ map->first_ip = first_ip;
+@@ -310,7 +310,7 @@ bitmap_ip_create(struct net *net, struct
+ if (!map)
+ return -ENOMEM;
+
+- map->memsize = bitmap_bytes(0, elements - 1);
++ map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long);
+ set->variant = &bitmap_ip;
+ if (!init_map_ip(set, map, first_ip, last_ip,
+ elements, hosts, netmask)) {
+--- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c
++++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c
+@@ -42,7 +42,7 @@ enum {
+
+ /* Type structure */
+ struct bitmap_ipmac {
+- void *members; /* the set members */
++ unsigned long *members; /* the set members */
+ 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 */
+@@ -299,7 +299,7 @@ static bool
+ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map,
+ u32 first_ip, u32 last_ip, u32 elements)
+ {
+- map->members = ip_set_alloc(map->memsize);
++ map->members = bitmap_zalloc(elements, GFP_KERNEL | __GFP_NOWARN);
+ if (!map->members)
+ return false;
+ map->first_ip = first_ip;
+@@ -360,7 +360,7 @@ bitmap_ipmac_create(struct net *net, str
+ if (!map)
+ return -ENOMEM;
+
+- map->memsize = bitmap_bytes(0, elements - 1);
++ map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long);
+ set->variant = &bitmap_ipmac;
+ if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) {
+ kfree(map);
+--- a/net/netfilter/ipset/ip_set_bitmap_port.c
++++ b/net/netfilter/ipset/ip_set_bitmap_port.c
+@@ -30,7 +30,7 @@ MODULE_ALIAS("ip_set_bitmap:port");
+
+ /* Type structure */
+ struct bitmap_port {
+- void *members; /* the set members */
++ unsigned long *members; /* the set members */
+ 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 */
+@@ -204,7 +204,7 @@ static bool
+ init_map_port(struct ip_set *set, struct bitmap_port *map,
+ u16 first_port, u16 last_port)
+ {
+- map->members = ip_set_alloc(map->memsize);
++ map->members = bitmap_zalloc(map->elements, GFP_KERNEL | __GFP_NOWARN);
+ if (!map->members)
+ return false;
+ map->first_port = first_port;
+@@ -244,7 +244,7 @@ bitmap_port_create(struct net *net, stru
+ return -ENOMEM;
+
+ map->elements = elements;
+- map->memsize = bitmap_bytes(0, map->elements);
++ map->memsize = BITS_TO_LONGS(elements) * sizeof(unsigned long);
+ set->variant = &bitmap_port;
+ if (!init_map_port(set, map, first_port, last_port)) {
+ kfree(map);
--- /dev/null
+From 826035498ec14b77b62a44f0cb6b94d45530db6f Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 21 Jan 2020 16:07:00 +0100
+Subject: netfilter: nf_tables: add __nft_chain_type_get()
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit 826035498ec14b77b62a44f0cb6b94d45530db6f upstream.
+
+This new helper function validates that unknown family and chain type
+coming from userspace do not trigger an out-of-bound array access. Bail
+out in case __nft_chain_type_get() returns NULL from
+nft_chain_parse_hook().
+
+Fixes: 9370761c56b6 ("netfilter: nf_tables: convert built-in tables/chains to chain types")
+Reported-by: syzbot+156a04714799b1d480bc@syzkaller.appspotmail.com
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/netfilter/nf_tables_api.c | 29 +++++++++++++++++++++--------
+ 1 file changed, 21 insertions(+), 8 deletions(-)
+
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -489,14 +489,27 @@ static inline u64 nf_tables_alloc_handle
+ static const struct nft_chain_type *chain_type[NFPROTO_NUMPROTO][NFT_CHAIN_T_MAX];
+
+ static const struct nft_chain_type *
++__nft_chain_type_get(u8 family, enum nft_chain_types type)
++{
++ if (family >= NFPROTO_NUMPROTO ||
++ type >= NFT_CHAIN_T_MAX)
++ return NULL;
++
++ return chain_type[family][type];
++}
++
++static const struct nft_chain_type *
+ __nf_tables_chain_type_lookup(const struct nlattr *nla, u8 family)
+ {
++ const struct nft_chain_type *type;
+ int i;
+
+ for (i = 0; i < NFT_CHAIN_T_MAX; i++) {
+- if (chain_type[family][i] != NULL &&
+- !nla_strcmp(nla, chain_type[family][i]->name))
+- return chain_type[family][i];
++ type = __nft_chain_type_get(family, i);
++ if (!type)
++ continue;
++ if (!nla_strcmp(nla, type->name))
++ return type;
+ }
+ return NULL;
+ }
+@@ -1095,11 +1108,8 @@ static void nf_tables_table_destroy(stru
+
+ void nft_register_chain_type(const struct nft_chain_type *ctype)
+ {
+- if (WARN_ON(ctype->family >= NFPROTO_NUMPROTO))
+- return;
+-
+ nfnl_lock(NFNL_SUBSYS_NFTABLES);
+- if (WARN_ON(chain_type[ctype->family][ctype->type] != NULL)) {
++ if (WARN_ON(__nft_chain_type_get(ctype->family, ctype->type))) {
+ nfnl_unlock(NFNL_SUBSYS_NFTABLES);
+ return;
+ }
+@@ -1551,7 +1561,10 @@ static int nft_chain_parse_hook(struct n
+ hook->num = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM]));
+ hook->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY]));
+
+- type = chain_type[family][NFT_CHAIN_T_DEFAULT];
++ type = __nft_chain_type_get(family, NFT_CHAIN_T_DEFAULT);
++ if (!type)
++ return -EOPNOTSUPP;
++
+ if (nla[NFTA_CHAIN_TYPE]) {
+ type = nf_tables_chain_type_lookup(net, nla[NFTA_CHAIN_TYPE],
+ family, autoload);
--- /dev/null
+From eb014de4fd418de1a277913cba244e47274fe392 Mon Sep 17 00:00:00 2001
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+Date: Tue, 21 Jan 2020 16:48:03 +0100
+Subject: netfilter: nf_tables: autoload modules from the abort path
+
+From: Pablo Neira Ayuso <pablo@netfilter.org>
+
+commit eb014de4fd418de1a277913cba244e47274fe392 upstream.
+
+This patch introduces a list of pending module requests. This new module
+list is composed of nft_module_request objects that contain the module
+name and one status field that tells if the module has been already
+loaded (the 'done' field).
+
+In the first pass, from the preparation phase, the netlink command finds
+that a module is missing on this list. Then, a module request is
+allocated and added to this list and nft_request_module() returns
+-EAGAIN. This triggers the abort path with the autoload parameter set on
+from nfnetlink, request_module() is called and the module request enters
+the 'done' state. Since the mutex is released when loading modules from
+the abort phase, the module list is zapped so this is iteration occurs
+over a local list. Therefore, the request_module() calls happen when
+object lists are in consistent state (after fulling aborting the
+transaction) and the commit list is empty.
+
+On the second pass, the netlink command will find that it already tried
+to load the module, so it does not request it again and
+nft_request_module() returns 0. Then, there is a look up to find the
+object that the command was missing. If the module was successfully
+loaded, the command proceeds normally since it finds the missing object
+in place, otherwise -ENOENT is reported to userspace.
+
+This patch also updates nfnetlink to include the reason to enter the
+abort phase, which is required for this new autoload module rationale.
+
+Fixes: ec7470b834fe ("netfilter: nf_tables: store transaction list locally while requesting module")
+Reported-by: syzbot+29125d208b3dae9a7019@syzkaller.appspotmail.com
+Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ include/linux/netfilter/nfnetlink.h | 2
+ include/net/netns/nftables.h | 1
+ net/netfilter/nf_tables_api.c | 126 ++++++++++++++++++++++++------------
+ net/netfilter/nfnetlink.c | 6 -
+ 4 files changed, 91 insertions(+), 44 deletions(-)
+
+--- a/include/linux/netfilter/nfnetlink.h
++++ b/include/linux/netfilter/nfnetlink.h
+@@ -31,7 +31,7 @@ struct nfnetlink_subsystem {
+ const struct nfnl_callback *cb; /* callback for individual types */
+ struct module *owner;
+ int (*commit)(struct net *net, struct sk_buff *skb);
+- int (*abort)(struct net *net, struct sk_buff *skb);
++ int (*abort)(struct net *net, struct sk_buff *skb, bool autoload);
+ void (*cleanup)(struct net *net);
+ bool (*valid_genid)(struct net *net, u32 genid);
+ };
+--- a/include/net/netns/nftables.h
++++ b/include/net/netns/nftables.h
+@@ -7,6 +7,7 @@
+ struct netns_nftables {
+ struct list_head tables;
+ struct list_head commit_list;
++ struct list_head module_list;
+ struct mutex commit_mutex;
+ unsigned int base_seq;
+ u8 gencursor;
+--- a/net/netfilter/nf_tables_api.c
++++ b/net/netfilter/nf_tables_api.c
+@@ -514,35 +514,45 @@ __nf_tables_chain_type_lookup(const stru
+ return NULL;
+ }
+
+-/*
+- * Loading a module requires dropping mutex that guards the transaction.
+- * A different client might race to start a new transaction meanwhile. Zap the
+- * list of pending transaction and then restore it once the mutex is grabbed
+- * again. Users of this function return EAGAIN which implicitly triggers the
+- * transaction abort path to clean up the list of pending transactions.
+- */
++struct nft_module_request {
++ struct list_head list;
++ char module[MODULE_NAME_LEN];
++ bool done;
++};
++
+ #ifdef CONFIG_MODULES
+-static void nft_request_module(struct net *net, const char *fmt, ...)
++static int nft_request_module(struct net *net, const char *fmt, ...)
+ {
+ char module_name[MODULE_NAME_LEN];
+- LIST_HEAD(commit_list);
++ struct nft_module_request *req;
+ va_list args;
+ int ret;
+
+- list_splice_init(&net->nft.commit_list, &commit_list);
+-
+ va_start(args, fmt);
+ ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
+ va_end(args);
+ if (ret >= MODULE_NAME_LEN)
+- return;
++ return 0;
+
+- mutex_unlock(&net->nft.commit_mutex);
+- request_module("%s", module_name);
+- mutex_lock(&net->nft.commit_mutex);
++ list_for_each_entry(req, &net->nft.module_list, list) {
++ if (!strcmp(req->module, module_name)) {
++ if (req->done)
++ return 0;
+
+- WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
+- list_splice(&commit_list, &net->nft.commit_list);
++ /* A request to load this module already exists. */
++ return -EAGAIN;
++ }
++ }
++
++ req = kmalloc(sizeof(*req), GFP_KERNEL);
++ if (!req)
++ return -ENOMEM;
++
++ req->done = false;
++ strlcpy(req->module, module_name, MODULE_NAME_LEN);
++ list_add_tail(&req->list, &net->nft.module_list);
++
++ return -EAGAIN;
+ }
+ #endif
+
+@@ -566,10 +576,9 @@ nf_tables_chain_type_lookup(struct net *
+ lockdep_nfnl_nft_mutex_not_held();
+ #ifdef CONFIG_MODULES
+ if (autoload) {
+- nft_request_module(net, "nft-chain-%u-%.*s", family,
+- nla_len(nla), (const char *)nla_data(nla));
+- type = __nf_tables_chain_type_lookup(nla, family);
+- if (type != NULL)
++ if (nft_request_module(net, "nft-chain-%u-%.*s", family,
++ nla_len(nla),
++ (const char *)nla_data(nla)) == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+ }
+ #endif
+@@ -2073,9 +2082,8 @@ static const struct nft_expr_type *__nft
+ static int nft_expr_type_request_module(struct net *net, u8 family,
+ struct nlattr *nla)
+ {
+- nft_request_module(net, "nft-expr-%u-%.*s", family,
+- nla_len(nla), (char *)nla_data(nla));
+- if (__nft_expr_type_get(family, nla))
++ if (nft_request_module(net, "nft-expr-%u-%.*s", family,
++ nla_len(nla), (char *)nla_data(nla)) == -EAGAIN)
+ return -EAGAIN;
+
+ return 0;
+@@ -2101,9 +2109,9 @@ static const struct nft_expr_type *nft_e
+ if (nft_expr_type_request_module(net, family, nla) == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+
+- nft_request_module(net, "nft-expr-%.*s",
+- nla_len(nla), (char *)nla_data(nla));
+- if (__nft_expr_type_get(family, nla))
++ if (nft_request_module(net, "nft-expr-%.*s",
++ nla_len(nla),
++ (char *)nla_data(nla)) == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+ }
+ #endif
+@@ -2194,9 +2202,10 @@ static int nf_tables_expr_parse(const st
+ err = PTR_ERR(ops);
+ #ifdef CONFIG_MODULES
+ if (err == -EAGAIN)
+- nft_expr_type_request_module(ctx->net,
+- ctx->family,
+- tb[NFTA_EXPR_NAME]);
++ if (nft_expr_type_request_module(ctx->net,
++ ctx->family,
++ tb[NFTA_EXPR_NAME]) != -EAGAIN)
++ err = -ENOENT;
+ #endif
+ goto err1;
+ }
+@@ -3033,8 +3042,7 @@ nft_select_set_ops(const struct nft_ctx
+ lockdep_nfnl_nft_mutex_not_held();
+ #ifdef CONFIG_MODULES
+ if (list_empty(&nf_tables_set_types)) {
+- nft_request_module(ctx->net, "nft-set");
+- if (!list_empty(&nf_tables_set_types))
++ if (nft_request_module(ctx->net, "nft-set") == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+ }
+ #endif
+@@ -5160,8 +5168,7 @@ nft_obj_type_get(struct net *net, u32 ob
+ lockdep_nfnl_nft_mutex_not_held();
+ #ifdef CONFIG_MODULES
+ if (type == NULL) {
+- nft_request_module(net, "nft-obj-%u", objtype);
+- if (__nft_obj_type_get(objtype))
++ if (nft_request_module(net, "nft-obj-%u", objtype) == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+ }
+ #endif
+@@ -5777,8 +5784,7 @@ nft_flowtable_type_get(struct net *net,
+ lockdep_nfnl_nft_mutex_not_held();
+ #ifdef CONFIG_MODULES
+ if (type == NULL) {
+- nft_request_module(net, "nf-flowtable-%u", family);
+- if (__nft_flowtable_type_get(family))
++ if (nft_request_module(net, "nf-flowtable-%u", family) == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+ }
+ #endif
+@@ -6725,6 +6731,18 @@ static void nft_chain_del(struct nft_cha
+ list_del_rcu(&chain->list);
+ }
+
++static void nf_tables_module_autoload_cleanup(struct net *net)
++{
++ struct nft_module_request *req, *next;
++
++ WARN_ON_ONCE(!list_empty(&net->nft.commit_list));
++ list_for_each_entry_safe(req, next, &net->nft.module_list, list) {
++ WARN_ON_ONCE(!req->done);
++ list_del(&req->list);
++ kfree(req);
++ }
++}
++
+ static void nf_tables_commit_release(struct net *net)
+ {
+ struct nft_trans *trans;
+@@ -6737,6 +6755,7 @@ static void nf_tables_commit_release(str
+ * to prevent expensive synchronize_rcu() in commit phase.
+ */
+ if (list_empty(&net->nft.commit_list)) {
++ nf_tables_module_autoload_cleanup(net);
+ mutex_unlock(&net->nft.commit_mutex);
+ return;
+ }
+@@ -6751,6 +6770,7 @@ static void nf_tables_commit_release(str
+ list_splice_tail_init(&net->nft.commit_list, &nf_tables_destroy_list);
+ spin_unlock(&nf_tables_destroy_list_lock);
+
++ nf_tables_module_autoload_cleanup(net);
+ mutex_unlock(&net->nft.commit_mutex);
+
+ schedule_work(&trans_destroy_work);
+@@ -6942,6 +6962,26 @@ static int nf_tables_commit(struct net *
+ return 0;
+ }
+
++static void nf_tables_module_autoload(struct net *net)
++{
++ struct nft_module_request *req, *next;
++ LIST_HEAD(module_list);
++
++ list_splice_init(&net->nft.module_list, &module_list);
++ mutex_unlock(&net->nft.commit_mutex);
++ list_for_each_entry_safe(req, next, &module_list, list) {
++ if (req->done) {
++ list_del(&req->list);
++ kfree(req);
++ } else {
++ request_module("%s", req->module);
++ req->done = true;
++ }
++ }
++ mutex_lock(&net->nft.commit_mutex);
++ list_splice(&module_list, &net->nft.module_list);
++}
++
+ static void nf_tables_abort_release(struct nft_trans *trans)
+ {
+ switch (trans->msg_type) {
+@@ -6971,7 +7011,7 @@ static void nf_tables_abort_release(stru
+ kfree(trans);
+ }
+
+-static int __nf_tables_abort(struct net *net)
++static int __nf_tables_abort(struct net *net, bool autoload)
+ {
+ struct nft_trans *trans, *next;
+ struct nft_trans_elem *te;
+@@ -7093,6 +7133,11 @@ static int __nf_tables_abort(struct net
+ nf_tables_abort_release(trans);
+ }
+
++ if (autoload)
++ nf_tables_module_autoload(net);
++ else
++ nf_tables_module_autoload_cleanup(net);
++
+ return 0;
+ }
+
+@@ -7101,9 +7146,9 @@ static void nf_tables_cleanup(struct net
+ nft_validate_state_update(net, NFT_VALIDATE_SKIP);
+ }
+
+-static int nf_tables_abort(struct net *net, struct sk_buff *skb)
++static int nf_tables_abort(struct net *net, struct sk_buff *skb, bool autoload)
+ {
+- int ret = __nf_tables_abort(net);
++ int ret = __nf_tables_abort(net, autoload);
+
+ mutex_unlock(&net->nft.commit_mutex);
+
+@@ -7698,6 +7743,7 @@ static int __net_init nf_tables_init_net
+ {
+ INIT_LIST_HEAD(&net->nft.tables);
+ INIT_LIST_HEAD(&net->nft.commit_list);
++ INIT_LIST_HEAD(&net->nft.module_list);
+ mutex_init(&net->nft.commit_mutex);
+ net->nft.base_seq = 1;
+ net->nft.validate_state = NFT_VALIDATE_SKIP;
+@@ -7709,7 +7755,7 @@ static void __net_exit nf_tables_exit_ne
+ {
+ mutex_lock(&net->nft.commit_mutex);
+ if (!list_empty(&net->nft.commit_list))
+- __nf_tables_abort(net);
++ __nf_tables_abort(net, false);
+ __nft_release_tables(net);
+ mutex_unlock(&net->nft.commit_mutex);
+ WARN_ON_ONCE(!list_empty(&net->nft.tables));
+--- a/net/netfilter/nfnetlink.c
++++ b/net/netfilter/nfnetlink.c
+@@ -476,7 +476,7 @@ ack:
+ }
+ done:
+ if (status & NFNL_BATCH_REPLAY) {
+- ss->abort(net, oskb);
++ ss->abort(net, oskb, true);
+ nfnl_err_reset(&err_list);
+ kfree_skb(skb);
+ module_put(ss->owner);
+@@ -487,11 +487,11 @@ done:
+ status |= NFNL_BATCH_REPLAY;
+ goto done;
+ } else if (err) {
+- ss->abort(net, oskb);
++ ss->abort(net, oskb, false);
+ netlink_ack(oskb, nlmsg_hdr(oskb), err, NULL);
+ }
+ } else {
+- ss->abort(net, oskb);
++ ss->abort(net, oskb, false);
+ }
+ if (ss->cleanup)
+ ss->cleanup(net);