dccp: fix tasklet usage [Linux 3.16.60]

This Linux kernel change "dccp: fix tasklet usage" is included in the Linux 3.16.60 release. This change is authored by Eric Dumazet <edumazet [at] google.com> on Thu May 3 09:39:20 2018 -0700. The commit for this change in Linux stable tree is 89fee40 (patch) which is from upstream commit a8d7aa1. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream a8d7aa1.

dccp: fix tasklet usage

commit a8d7aa17bbc970971ccdf71988ea19230ab368b1 upstream.

syzbot reported a crash in tasklet_action_common() caused by dccp.

dccp needs to make sure socket wont disappear before tasklet handler
has completed.

This patch takes a reference on the socket when arming the tasklet,
and moves the sock_put() from dccp_write_xmit_timer() to dccp_write_xmitlet()

kernel BUG at kernel/softirq.c:514!
invalid opcode: 0000 [#1] SMP KASAN
Dumping ftrace buffer:
   (ftrace buffer empty)
Modules linked in:
CPU: 1 PID: 17 Comm: ksoftirqd/1 Not tainted 4.17.0-rc3+ #30
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
RIP: 0010:tasklet_action_common.isra.19+0x6db/0x700 kernel/softirq.c:515
RSP: 0018:ffff8801d9b3faf8 EFLAGS: 00010246
dccp_close: ABORT with 65423 bytes unread
RAX: 1ffff1003b367f6b RBX: ffff8801daf1f3f0 RCX: 0000000000000000
RDX: ffff8801cf895498 RSI: 0000000000000004 RDI: 0000000000000000
RBP: ffff8801d9b3fc40 R08: ffffed0039f12a95 R09: ffffed0039f12a94
dccp_close: ABORT with 65423 bytes unread
R10: ffffed0039f12a94 R11: ffff8801cf8954a3 R12: 0000000000000000
R13: ffff8801d9b3fc18 R14: dffffc0000000000 R15: ffff8801cf895490
FS:  0000000000000000(0000) GS:ffff8801daf00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000001b2bc28000 CR3: 00000001a08a9000 CR4: 00000000001406e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 tasklet_action+0x1d/0x20 kernel/softirq.c:533
 __do_softirq+0x2e0/0xaf5 kernel/softirq.c:285
dccp_close: ABORT with 65423 bytes unread
 run_ksoftirqd+0x86/0x100 kernel/softirq.c:646
 smpboot_thread_fn+0x417/0x870 kernel/smpboot.c:164
 kthread+0x345/0x410 kernel/kthread.c:238
 ret_from_fork+0x3a/0x50 arch/x86/entry/entry_64.S:412
Code: 48 8b 85 e8 fe ff ff 48 8b 95 f0 fe ff ff e9 94 fb ff ff 48 89 95 f0 fe ff ff e8 81 53 6e 00 48 8b 95 f0 fe ff ff e9 62 fb ff ff <0f> 0b 48 89 cf 48 89 8d e8 fe ff ff e8 64 53 6e 00 48 8b 8d e8
RIP: tasklet_action_common.isra.19+0x6db/0x700 kernel/softirq.c:515 RSP: ffff8801d9b3faf8

Fixes: dc841e30eaea ("dccp: Extend CCID packet dequeueing interface")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: syzbot <syzkaller@googlegroups.com>
Cc: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Cc: dccp@vger.kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backported to 3.16: Timer parameter is still an unsigned long]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>

There are 16 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/dccp/ccids/ccid2.c | 14 ++++++++++++--
 net/dccp/timer.c       |  2 +-
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
index 4dbea29..e66e9f2 100644
--- a/net/dccp/ccids/ccid2.c
+++ b/net/dccp/ccids/ccid2.c
@@ -126,6 +126,16 @@ static void ccid2_change_l_seq_window(struct sock *sk, u64 val)
                          DCCPF_SEQ_WMAX));
 }

+static void dccp_tasklet_schedule(struct sock *sk)
+{
+   struct tasklet_struct *t = &dccp_sk(sk)->dccps_xmitlet;
+
+   if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
+       sock_hold(sk);
+       __tasklet_schedule(t);
+   }
+}
+
 static void ccid2_hc_tx_rto_expire(unsigned long data)
 {
    struct sock *sk = (struct sock *)data;
@@ -166,7 +176,7 @@ static void ccid2_hc_tx_rto_expire(unsigned long data)

    /* if we were blocked before, we may now send cwnd=1 packet */
    if (sender_was_blocked)
-       tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+       dccp_tasklet_schedule(sk);
    /* restart backed-off timer */
    sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
 out:
@@ -706,7 +716,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
 done:
    /* check if incoming Acks allow pending packets to be sent */
    if (sender_was_blocked && !ccid2_cwnd_network_limited(hc))
-       tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet);
+       dccp_tasklet_schedule(sk);
    dccp_ackvec_parsed_cleanup(&hc->tx_av_chunks);
 }

diff --git a/net/dccp/timer.c b/net/dccp/timer.c
index 1cd46a3..851a212 100644
--- a/net/dccp/timer.c
+++ b/net/dccp/timer.c
@@ -252,12 +252,12 @@ static void dccp_write_xmitlet(unsigned long data)
    else
        dccp_write_xmit(sk);
    bh_unlock_sock(sk);
+   sock_put(sk);
 }

 static void dccp_write_xmit_timer(unsigned long data)
 {
    dccp_write_xmitlet(data);
-   sock_put((struct sock *)data);
 }

 void dccp_init_xmit_timers(struct sock *sk)

Leave a Reply

Your email address will not be published. Required fields are marked *