| From foo@baz Fri Apr 27 12:23:41 CEST 2018 |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| Date: Thu, 19 Apr 2018 12:25:38 -0700 |
| Subject: llc: delete timers synchronously in llc_sk_free() |
| |
| From: Cong Wang <xiyou.wangcong@gmail.com> |
| |
| [ Upstream commit b905ef9ab90115d001c1658259af4b1c65088779 ] |
| |
| The connection timers of an llc sock could be still flying |
| after we delete them in llc_sk_free(), and even possibly |
| after we free the sock. We could just wait synchronously |
| here in case of troubles. |
| |
| Note, I leave other call paths as they are, since they may |
| not have to wait, at least we can change them to synchronously |
| when needed. |
| |
| Also, move the code to net/llc/llc_conn.c, which is apparently |
| a better place. |
| |
| Reported-by: <syzbot+f922284c18ea23a8e457@syzkaller.appspotmail.com> |
| Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/net/llc_conn.h | 1 + |
| net/llc/llc_c_ac.c | 9 +-------- |
| net/llc/llc_conn.c | 22 +++++++++++++++++++++- |
| 3 files changed, 23 insertions(+), 9 deletions(-) |
| |
| --- a/include/net/llc_conn.h |
| +++ b/include/net/llc_conn.h |
| @@ -97,6 +97,7 @@ static __inline__ char llc_backlog_type( |
| |
| struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority, |
| struct proto *prot, int kern); |
| +void llc_sk_stop_all_timers(struct sock *sk, bool sync); |
| void llc_sk_free(struct sock *sk); |
| |
| void llc_sk_reset(struct sock *sk); |
| --- a/net/llc/llc_c_ac.c |
| +++ b/net/llc/llc_c_ac.c |
| @@ -1096,14 +1096,7 @@ int llc_conn_ac_inc_tx_win_size(struct s |
| |
| int llc_conn_ac_stop_all_timers(struct sock *sk, struct sk_buff *skb) |
| { |
| - struct llc_sock *llc = llc_sk(sk); |
| - |
| - del_timer(&llc->pf_cycle_timer.timer); |
| - del_timer(&llc->ack_timer.timer); |
| - del_timer(&llc->rej_sent_timer.timer); |
| - del_timer(&llc->busy_state_timer.timer); |
| - llc->ack_must_be_send = 0; |
| - llc->ack_pf = 0; |
| + llc_sk_stop_all_timers(sk, false); |
| return 0; |
| } |
| |
| --- a/net/llc/llc_conn.c |
| +++ b/net/llc/llc_conn.c |
| @@ -951,6 +951,26 @@ out: |
| return sk; |
| } |
| |
| +void llc_sk_stop_all_timers(struct sock *sk, bool sync) |
| +{ |
| + struct llc_sock *llc = llc_sk(sk); |
| + |
| + if (sync) { |
| + del_timer_sync(&llc->pf_cycle_timer.timer); |
| + del_timer_sync(&llc->ack_timer.timer); |
| + del_timer_sync(&llc->rej_sent_timer.timer); |
| + del_timer_sync(&llc->busy_state_timer.timer); |
| + } else { |
| + del_timer(&llc->pf_cycle_timer.timer); |
| + del_timer(&llc->ack_timer.timer); |
| + del_timer(&llc->rej_sent_timer.timer); |
| + del_timer(&llc->busy_state_timer.timer); |
| + } |
| + |
| + llc->ack_must_be_send = 0; |
| + llc->ack_pf = 0; |
| +} |
| + |
| /** |
| * llc_sk_free - Frees a LLC socket |
| * @sk - socket to free |
| @@ -963,7 +983,7 @@ void llc_sk_free(struct sock *sk) |
| |
| llc->state = LLC_CONN_OUT_OF_SVC; |
| /* Stop all (possibly) running timers */ |
| - llc_conn_ac_stop_all_timers(sk, NULL); |
| + llc_sk_stop_all_timers(sk, true); |
| #ifdef DEBUG_LLC_CONN_ALLOC |
| printk(KERN_INFO "%s: unackq=%d, txq=%d\n", __func__, |
| skb_queue_len(&llc->pdu_unack_q), |