Skip to content

Commit 4b5bb77

Browse files
lxingregkh
authored andcommitted
sctp: reset owner sk for data chunks on out queues when migrating a sock
[ Upstream commit d04adf1b355181e737b6b1e23d801b07f0b7c4c0 ] Now when migrating sock to another one in sctp_sock_migrate(), it only resets owner sk for the data in receive queues, not the chunks on out queues. It would cause that data chunks length on the sock is not consistent with sk sk_wmem_alloc. When closing the sock or freeing these chunks, the old sk would never be freed, and the new sock may crash due to the overflow sk_wmem_alloc. syzbot found this issue with this series: r0 = socket$inet_sctp() sendto$inet(r0) listen(r0) accept4(r0) close(r0) Although listen() should have returned error when one TCP-style socket is in connecting (I may fix this one in another patch), it could also be reproduced by peeling off an assoc. This issue is there since very beginning. This patch is to reset owner sk for the chunks on out queues so that sk sk_wmem_alloc has correct value after accept one sock or peeloff an assoc to one sock. Note that when resetting owner sk for chunks on outqueue, it has to sctp_clear_owner_w/skb_orphan chunks before changing assoc->base.sk first and then sctp_set_owner_w them after changing assoc->base.sk, due to that sctp_wfree and it's callees are using assoc->base.sk. Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Xin Long <lucien.xin@gmail.com> Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent bcb3b90 commit 4b5bb77

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

net/sctp/socket.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,36 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
168168
sk_mem_charge(sk, chunk->skb->truesize);
169169
}
170170

171+
static void sctp_clear_owner_w(struct sctp_chunk *chunk)
172+
{
173+
skb_orphan(chunk->skb);
174+
}
175+
176+
static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
177+
void (*cb)(struct sctp_chunk *))
178+
179+
{
180+
struct sctp_outq *q = &asoc->outqueue;
181+
struct sctp_transport *t;
182+
struct sctp_chunk *chunk;
183+
184+
list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
185+
list_for_each_entry(chunk, &t->transmitted, transmitted_list)
186+
cb(chunk);
187+
188+
list_for_each_entry(chunk, &q->retransmit, list)
189+
cb(chunk);
190+
191+
list_for_each_entry(chunk, &q->sacked, list)
192+
cb(chunk);
193+
194+
list_for_each_entry(chunk, &q->abandoned, list)
195+
cb(chunk);
196+
197+
list_for_each_entry(chunk, &q->out_chunk_list, list)
198+
cb(chunk);
199+
}
200+
171201
/* Verify that this is a valid address. */
172202
static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr,
173203
int len)
@@ -7362,7 +7392,9 @@ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
73627392
* paths won't try to lock it and then oldsk.
73637393
*/
73647394
lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
7395+
sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w);
73657396
sctp_assoc_migrate(assoc, newsk);
7397+
sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w);
73667398

73677399
/* If the association on the newsk is already closed before accept()
73687400
* is called, set RCV_SHUTDOWN flag.

0 commit comments

Comments
 (0)