From: Greg Kroah-Hartman Date: Tue, 28 Jan 2020 09:31:40 +0000 (+0100) Subject: 5.4-stable patches X-Git-Tag: v4.4.212~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1d14c1b256a4a64a1b0ebf0aa24b17b9278ff129;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: net-x25-fix-nonblocking-connect.patch netfilter-ipset-use-bitmap-infrastructure-completely.patch netfilter-nf_tables-add-__nft_chain_type_get.patch netfilter-nf_tables-autoload-modules-from-the-abort-path.patch --- diff --git a/queue-5.4/net-x25-fix-nonblocking-connect.patch b/queue-5.4/net-x25-fix-nonblocking-connect.patch new file mode 100644 index 00000000000..8a43928ce30 --- /dev/null +++ b/queue-5.4/net-x25-fix-nonblocking-connect.patch @@ -0,0 +1,56 @@ +From e21dba7a4df4d93da237da65a096084b4f2e87b4 Mon Sep 17 00:00:00 2001 +From: Martin Schiller +Date: Thu, 9 Jan 2020 07:31:14 +0100 +Subject: net/x25: fix nonblocking connect + +From: Martin Schiller + +commit e21dba7a4df4d93da237da65a096084b4f2e87b4 upstream. + +This patch fixes 2 issues in x25_connect(): + +1. It makes absolutely no sense to reset the neighbour and the +connection state after a (successful) nonblocking call of x25_connect. +This prevents any connection from being established, since the response +(call accept) cannot be processed. + +2. Any further calls to x25_connect() while a call is pending should +simply return, instead of creating new Call Request (on different +logical channels). + +This patch should also fix the "KASAN: null-ptr-deref Write in +x25_connect" and "BUG: unable to handle kernel NULL pointer dereference +in x25_connect" bugs reported by syzbot. + +Signed-off-by: Martin Schiller +Reported-by: syzbot+429c200ffc8772bfe070@syzkaller.appspotmail.com +Reported-by: syzbot+eec0c87f31a7c3b66f7b@syzkaller.appspotmail.com +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + net/x25/af_x25.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/net/x25/af_x25.c ++++ b/net/x25/af_x25.c +@@ -760,6 +760,10 @@ static int x25_connect(struct socket *so + if (sk->sk_state == TCP_ESTABLISHED) + goto out; + ++ rc = -EALREADY; /* Do nothing if call is already in progress */ ++ if (sk->sk_state == TCP_SYN_SENT) ++ goto out; ++ + sk->sk_state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + +@@ -806,7 +810,7 @@ static int x25_connect(struct socket *so + /* Now the loop */ + rc = -EINPROGRESS; + if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) +- goto out_put_neigh; ++ goto out; + + rc = x25_wait_for_connection_establishment(sk); + if (rc) diff --git a/queue-5.4/netfilter-ipset-use-bitmap-infrastructure-completely.patch b/queue-5.4/netfilter-ipset-use-bitmap-infrastructure-completely.patch new file mode 100644 index 00000000000..9a5cb9cb7e8 --- /dev/null +++ b/queue-5.4/netfilter-ipset-use-bitmap-infrastructure-completely.patch @@ -0,0 +1,147 @@ +From 32c72165dbd0e246e69d16a3ad348a4851afd415 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Kadlecsik=20J=C3=B3zsef?= +Date: Sun, 19 Jan 2020 22:06:49 +0100 +Subject: netfilter: ipset: use bitmap infrastructure completely + +From: Kadlecsik József + +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 +Signed-off-by: Pablo Neira Ayuso +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-5.4/netfilter-nf_tables-add-__nft_chain_type_get.patch b/queue-5.4/netfilter-nf_tables-add-__nft_chain_type_get.patch new file mode 100644 index 00000000000..14b9b9ff698 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-add-__nft_chain_type_get.patch @@ -0,0 +1,81 @@ +From 826035498ec14b77b62a44f0cb6b94d45530db6f Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 21 Jan 2020 16:07:00 +0100 +Subject: netfilter: nf_tables: add __nft_chain_type_get() + +From: Pablo Neira Ayuso + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-5.4/netfilter-nf_tables-autoload-modules-from-the-abort-path.patch b/queue-5.4/netfilter-nf_tables-autoload-modules-from-the-abort-path.patch new file mode 100644 index 00000000000..dd916ce6ee8 --- /dev/null +++ b/queue-5.4/netfilter-nf_tables-autoload-modules-from-the-abort-path.patch @@ -0,0 +1,353 @@ +From eb014de4fd418de1a277913cba244e47274fe392 Mon Sep 17 00:00:00 2001 +From: Pablo Neira Ayuso +Date: Tue, 21 Jan 2020 16:48:03 +0100 +Subject: netfilter: nf_tables: autoload modules from the abort path + +From: Pablo Neira Ayuso + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-5.4/series b/queue-5.4/series index 36ac0844fb5..137f9035f68 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -98,3 +98,7 @@ net-sonic-fix-cam-initialization.patch net-sonic-prevent-tx-watchdog-timeout.patch libertas-fix-two-buffer-overflows-at-parsing-bss-descriptor.patch media-v4l2-ioctl.c-zero-reserved-fields-for-s-try_fmt.patch +netfilter-ipset-use-bitmap-infrastructure-completely.patch +netfilter-nf_tables-add-__nft_chain_type_get.patch +netfilter-nf_tables-autoload-modules-from-the-abort-path.patch +net-x25-fix-nonblocking-connect.patch