From: Sasha Levin Date: Mon, 15 Jun 2020 03:27:38 +0000 (-0400) Subject: Fixes for 5.4 X-Git-Tag: v5.4.47~114^2~1 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=c9354aebb12fd1c70d517b899ee39cbaca6f7dbb;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.4 Signed-off-by: Sasha Levin --- diff --git a/queue-5.4/ipv4-fix-a-rcu-list-lock-in-fib_triestat_seq_show.patch b/queue-5.4/ipv4-fix-a-rcu-list-lock-in-fib_triestat_seq_show.patch new file mode 100644 index 00000000000..3b8563401ef --- /dev/null +++ b/queue-5.4/ipv4-fix-a-rcu-list-lock-in-fib_triestat_seq_show.patch @@ -0,0 +1,68 @@ +From 45196976f627fc6e08aafd469d579d3d465a8cc3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 25 Mar 2020 18:01:00 -0400 +Subject: ipv4: fix a RCU-list lock in fib_triestat_seq_show + +From: Qian Cai + +[ Upstream commit fbe4e0c1b298b4665ee6915266c9d6c5b934ef4a ] + +fib_triestat_seq_show() calls hlist_for_each_entry_rcu(tb, head, +tb_hlist) without rcu_read_lock() will trigger a warning, + + net/ipv4/fib_trie.c:2579 RCU-list traversed in non-reader section!! + + other info that might help us debug this: + + rcu_scheduler_active = 2, debug_locks = 1 + 1 lock held by proc01/115277: + #0: c0000014507acf00 (&p->lock){+.+.}-{3:3}, at: seq_read+0x58/0x670 + + Call Trace: + dump_stack+0xf4/0x164 (unreliable) + lockdep_rcu_suspicious+0x140/0x164 + fib_triestat_seq_show+0x750/0x880 + seq_read+0x1a0/0x670 + proc_reg_read+0x10c/0x1b0 + __vfs_read+0x3c/0x70 + vfs_read+0xac/0x170 + ksys_read+0x7c/0x140 + system_call+0x5c/0x68 + +Fix it by adding a pair of rcu_read_lock/unlock() and use +cond_resched_rcu() to avoid the situation where walking of a large +number of items may prevent scheduling for a long time. + +Signed-off-by: Qian Cai +Reviewed-by: Eric Dumazet +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/ipv4/fib_trie.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c +index f12fa8da6127..1b851fd82613 100644 +--- a/net/ipv4/fib_trie.c ++++ b/net/ipv4/fib_trie.c +@@ -2455,6 +2455,7 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) + " %zd bytes, size of tnode: %zd bytes.\n", + LEAF_SIZE, TNODE_SIZE(0)); + ++ rcu_read_lock(); + for (h = 0; h < FIB_TABLE_HASHSZ; h++) { + struct hlist_head *head = &net->ipv4.fib_table_hash[h]; + struct fib_table *tb; +@@ -2474,7 +2475,9 @@ static int fib_triestat_seq_show(struct seq_file *seq, void *v) + trie_show_usage(seq, t->stats); + #endif + } ++ cond_resched_rcu(); + } ++ rcu_read_unlock(); + + return 0; + } +-- +2.25.1 + diff --git a/queue-5.4/iwlwifi-mvm-fix-nvm-check-for-3168-devices.patch b/queue-5.4/iwlwifi-mvm-fix-nvm-check-for-3168-devices.patch new file mode 100644 index 00000000000..849d48d67c9 --- /dev/null +++ b/queue-5.4/iwlwifi-mvm-fix-nvm-check-for-3168-devices.patch @@ -0,0 +1,39 @@ +From 53988ea786ba98a9f203262a308cc1a1aad8d11f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 25 Nov 2019 13:21:58 +0200 +Subject: iwlwifi: mvm: fix NVM check for 3168 devices + +From: Luca Coelho + +[ Upstream commit b3f20e098293892388d6a0491d6bbb2efb46fbff ] + +We had a check on !NVM_EXT and then a check for NVM_SDP in the else +block of this if. The else block, obviously, could only be reached if +using NVM_EXT, so it would never be NVM_SDP. + +Fix that by checking whether the nvm_type is IWL_NVM instead of +checking for !IWL_NVM_EXT to solve this issue. + +Reported-by: Stefan Sperling +Signed-off-by: Luca Coelho +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +index ed367b0a185c..f49887379c43 100644 +--- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c ++++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +@@ -281,7 +281,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) + int regulatory_type; + + /* Checking for required sections */ +- if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) { ++ if (mvm->trans->cfg->nvm_type == IWL_NVM) { + if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || + !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { + IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); +-- +2.25.1 + diff --git a/queue-5.4/sctp-fix-possibly-using-a-bad-saddr-with-a-given-dst.patch b/queue-5.4/sctp-fix-possibly-using-a-bad-saddr-with-a-given-dst.patch new file mode 100644 index 00000000000..43858f1ff25 --- /dev/null +++ b/queue-5.4/sctp-fix-possibly-using-a-bad-saddr-with-a-given-dst.patch @@ -0,0 +1,195 @@ +From e9c5ef5d7ff9547c42923ebff7858fb15a43e44c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 26 Mar 2020 20:47:46 -0300 +Subject: sctp: fix possibly using a bad saddr with a given dst + +From: Marcelo Ricardo Leitner + +[ Upstream commit 582eea230536a6f104097dd46205822005d5fe3a ] + +Under certain circumstances, depending on the order of addresses on the +interfaces, it could be that sctp_v[46]_get_dst() would return a dst +with a mismatched struct flowi. + +For example, if when walking through the bind addresses and the first +one is not a match, it saves the dst as a fallback (added in +410f03831c07), but not the flowi. Then if the next one is also not a +match, the previous dst will be returned but with the flowi information +for the 2nd address, which is wrong. + +The fix is to use a locally stored flowi that can be used for such +attempts, and copy it to the parameter only in case it is a possible +match, together with the corresponding dst entry. + +The patch updates IPv6 code mostly just to be in sync. Even though the issue +is also present there, it fallback is not expected to work with IPv6. + +Fixes: 410f03831c07 ("sctp: add routing output fallback") +Reported-by: Jin Meng +Signed-off-by: Marcelo Ricardo Leitner +Tested-by: Xin Long +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/sctp/ipv6.c | 20 ++++++++++++++------ + net/sctp/protocol.c | 28 +++++++++++++++++++--------- + 2 files changed, 33 insertions(+), 15 deletions(-) + +diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c +index bc734cfaa29e..c87af430107a 100644 +--- a/net/sctp/ipv6.c ++++ b/net/sctp/ipv6.c +@@ -228,7 +228,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + { + struct sctp_association *asoc = t->asoc; + struct dst_entry *dst = NULL; +- struct flowi6 *fl6 = &fl->u.ip6; ++ struct flowi _fl; ++ struct flowi6 *fl6 = &_fl.u.ip6; + struct sctp_bind_addr *bp; + struct ipv6_pinfo *np = inet6_sk(sk); + struct sctp_sockaddr_entry *laddr; +@@ -238,7 +239,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + enum sctp_scope scope; + __u8 matchlen = 0; + +- memset(fl6, 0, sizeof(struct flowi6)); ++ memset(&_fl, 0, sizeof(_fl)); + fl6->daddr = daddr->v6.sin6_addr; + fl6->fl6_dport = daddr->v6.sin6_port; + fl6->flowi6_proto = IPPROTO_SCTP; +@@ -276,8 +277,11 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + rcu_read_unlock(); + + dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p); +- if (!asoc || saddr) ++ if (!asoc || saddr) { ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); + goto out; ++ } + + bp = &asoc->base.bind_addr; + scope = sctp_scope(daddr); +@@ -300,6 +304,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + if ((laddr->a.sa.sa_family == AF_INET6) && + (sctp_v6_cmp_addr(&dst_saddr, &laddr->a))) { + rcu_read_unlock(); ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); + goto out; + } + } +@@ -338,6 +344,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + if (!IS_ERR_OR_NULL(dst)) + dst_release(dst); + dst = bdst; ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); + break; + } + +@@ -351,6 +359,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + dst_release(dst); + dst = bdst; + matchlen = bmatchlen; ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); + } + rcu_read_unlock(); + +@@ -359,14 +369,12 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + struct rt6_info *rt; + + rt = (struct rt6_info *)dst; +- t->dst = dst; + t->dst_cookie = rt6_get_cookie(rt); + pr_debug("rt6_dst:%pI6/%d rt6_src:%pI6\n", + &rt->rt6i_dst.addr, rt->rt6i_dst.plen, +- &fl6->saddr); ++ &fl->u.ip6.saddr); + } else { + t->dst = NULL; +- + pr_debug("no route\n"); + } + } +diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c +index 681ffb3545db..237c88eeb538 100644 +--- a/net/sctp/protocol.c ++++ b/net/sctp/protocol.c +@@ -409,7 +409,8 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + { + struct sctp_association *asoc = t->asoc; + struct rtable *rt; +- struct flowi4 *fl4 = &fl->u.ip4; ++ struct flowi _fl; ++ struct flowi4 *fl4 = &_fl.u.ip4; + struct sctp_bind_addr *bp; + struct sctp_sockaddr_entry *laddr; + struct dst_entry *dst = NULL; +@@ -419,7 +420,7 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + + if (t->dscp & SCTP_DSCP_SET_MASK) + tos = t->dscp & SCTP_DSCP_VAL_MASK; +- memset(fl4, 0x0, sizeof(struct flowi4)); ++ memset(&_fl, 0x0, sizeof(_fl)); + fl4->daddr = daddr->v4.sin_addr.s_addr; + fl4->fl4_dport = daddr->v4.sin_port; + fl4->flowi4_proto = IPPROTO_SCTP; +@@ -438,8 +439,11 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + &fl4->saddr); + + rt = ip_route_output_key(sock_net(sk), fl4); +- if (!IS_ERR(rt)) ++ if (!IS_ERR(rt)) { + dst = &rt->dst; ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); ++ } + + /* If there is no association or if a source address is passed, no + * more validation is required. +@@ -502,27 +506,33 @@ static void sctp_v4_get_dst(struct sctp_transport *t, union sctp_addr *saddr, + odev = __ip_dev_find(sock_net(sk), laddr->a.v4.sin_addr.s_addr, + false); + if (!odev || odev->ifindex != fl4->flowi4_oif) { +- if (!dst) ++ if (!dst) { + dst = &rt->dst; +- else ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); ++ } else { + dst_release(&rt->dst); ++ } + continue; + } + + dst_release(dst); + dst = &rt->dst; ++ t->dst = dst; ++ memcpy(fl, &_fl, sizeof(_fl)); + break; + } + + out_unlock: + rcu_read_unlock(); + out: +- t->dst = dst; +- if (dst) ++ if (dst) { + pr_debug("rt_dst:%pI4, rt_src:%pI4\n", +- &fl4->daddr, &fl4->saddr); +- else ++ &fl->u.ip4.daddr, &fl->u.ip4.saddr); ++ } else { ++ t->dst = NULL; + pr_debug("no route\n"); ++ } + } + + /* For v4, the source address is cached in the route entry(dst). So no need +-- +2.25.1 + diff --git a/queue-5.4/sctp-fix-refcount-bug-in-sctp_wfree.patch b/queue-5.4/sctp-fix-refcount-bug-in-sctp_wfree.patch new file mode 100644 index 00000000000..39684aad35f --- /dev/null +++ b/queue-5.4/sctp-fix-refcount-bug-in-sctp_wfree.patch @@ -0,0 +1,120 @@ +From 5a54044cb23fbb3bf8614995014782779a975738 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 Mar 2020 11:07:51 +0800 +Subject: sctp: fix refcount bug in sctp_wfree + +From: Qiujun Huang + +[ Upstream commit 5c3e82fe159622e46e91458c1a6509c321a62820 ] + +We should iterate over the datamsgs to move +all chunks(skbs) to newsk. + +The following case cause the bug: +for the trouble SKB, it was in outq->transmitted list + +sctp_outq_sack + sctp_check_transmitted + SKB was moved to outq->sacked list + then throw away the sack queue + SKB was deleted from outq->sacked +(but it was held by datamsg at sctp_datamsg_to_asoc +So, sctp_wfree was not called here) + +then migrate happened + + sctp_for_each_tx_datachunk( + sctp_clear_owner_w); + sctp_assoc_migrate(); + sctp_for_each_tx_datachunk( + sctp_set_owner_w); +SKB was not in the outq, and was not changed to newsk + +finally + +__sctp_outq_teardown + sctp_chunk_put (for another skb) + sctp_datamsg_put + __kfree_skb(msg->frag_list) + sctp_wfree (for SKB) + SKB->sk was still oldsk (skb->sk != asoc->base.sk). + +Reported-and-tested-by: syzbot+cea71eec5d6de256d54d@syzkaller.appspotmail.com +Signed-off-by: Qiujun Huang +Acked-by: Marcelo Ricardo Leitner +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + net/sctp/socket.c | 31 +++++++++++++++++++++++-------- + 1 file changed, 23 insertions(+), 8 deletions(-) + +diff --git a/net/sctp/socket.c b/net/sctp/socket.c +index ffd3262b7a41..58fe6556cdf5 100644 +--- a/net/sctp/socket.c ++++ b/net/sctp/socket.c +@@ -147,29 +147,44 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk) + skb_orphan(chunk->skb); + } + ++#define traverse_and_process() \ ++do { \ ++ msg = chunk->msg; \ ++ if (msg == prev_msg) \ ++ continue; \ ++ list_for_each_entry(c, &msg->chunks, frag_list) { \ ++ if ((clear && asoc->base.sk == c->skb->sk) || \ ++ (!clear && asoc->base.sk != c->skb->sk)) \ ++ cb(c); \ ++ } \ ++ prev_msg = msg; \ ++} while (0) ++ + static void sctp_for_each_tx_datachunk(struct sctp_association *asoc, ++ bool clear, + void (*cb)(struct sctp_chunk *)) + + { ++ struct sctp_datamsg *msg, *prev_msg = NULL; + struct sctp_outq *q = &asoc->outqueue; ++ struct sctp_chunk *chunk, *c; + struct sctp_transport *t; +- struct sctp_chunk *chunk; + + list_for_each_entry(t, &asoc->peer.transport_addr_list, transports) + list_for_each_entry(chunk, &t->transmitted, transmitted_list) +- cb(chunk); ++ traverse_and_process(); + + list_for_each_entry(chunk, &q->retransmit, transmitted_list) +- cb(chunk); ++ traverse_and_process(); + + list_for_each_entry(chunk, &q->sacked, transmitted_list) +- cb(chunk); ++ traverse_and_process(); + + list_for_each_entry(chunk, &q->abandoned, transmitted_list) +- cb(chunk); ++ traverse_and_process(); + + list_for_each_entry(chunk, &q->out_chunk_list, list) +- cb(chunk); ++ traverse_and_process(); + } + + static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk, +@@ -9461,9 +9476,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, + * paths won't try to lock it and then oldsk. + */ + lock_sock_nested(newsk, SINGLE_DEPTH_NESTING); +- sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w); ++ sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w); + sctp_assoc_migrate(assoc, newsk); +- sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w); ++ sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w); + + /* If the association on the newsk is already closed before accept() + * is called, set RCV_SHUTDOWN flag. +-- +2.25.1 + diff --git a/queue-5.4/series b/queue-5.4/series index 37870de9a20..13c46aca0fc 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -30,3 +30,7 @@ perf-probe-accept-the-instance-number-of-kretprobe-e.patch mm-add-kvfree_sensitive-for-freeing-sensitive-data-o.patch selftests-fix-flower-parent-qdisc.patch fanotify-fix-ignore-mask-logic-for-events-on-child-a.patch +ipv4-fix-a-rcu-list-lock-in-fib_triestat_seq_show.patch +iwlwifi-mvm-fix-nvm-check-for-3168-devices.patch +sctp-fix-possibly-using-a-bad-saddr-with-a-given-dst.patch +sctp-fix-refcount-bug-in-sctp_wfree.patch