mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
sctp: re-point retained control chunks on association migration
sctp_sock_migrate() transfers DATA chunk ownership but leaves retained
control chunks pointing at the old socket. A later retransmission can
therefore access the socket after it has been freed.
Extend the migration walk to cover retained control chunks and use
sctp_control_set_owner_w() to assign their new owner. Reuse the DATA
ownership check so each chunk is processed once per pass, even when it
is reachable through multiple lists or retained pointers.
Preserve a control chunk's existing authentication key across migration
so its final release can still generate SCTP_AUTH_FREE_KEY when needed.
Fixes: d04adf1b35 ("sctp: reset owner sk for data chunks on out queues when migrating a sock")
Cc: stable@kernel.org
Reported-by: TencentOS Corvus AI <corvus@tencent.com>
Signed-off-by: Jun Yang <junvyyang@tencent.com>
Acked-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/20261009074244.3718-3-juny24602@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
ad1320865b
commit
52ffeaa358
|
|
@ -252,6 +252,7 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
|
|||
struct sctp_fwdtsn_skip *skiplist);
|
||||
struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
|
||||
__u16 key_id);
|
||||
void sctp_control_set_owner_w(struct sctp_chunk *chunk);
|
||||
struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc,
|
||||
__u16 stream_num, __be16 *stream_list,
|
||||
bool out, bool in);
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ static void sctp_control_release_owner(struct sk_buff *skb)
|
|||
}
|
||||
}
|
||||
|
||||
static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
|
||||
void sctp_control_set_owner_w(struct sctp_chunk *chunk)
|
||||
{
|
||||
struct sctp_association *asoc = chunk->asoc;
|
||||
struct sk_buff *skb = chunk->skb;
|
||||
|
|
@ -107,7 +107,8 @@ static void sctp_control_set_owner_w(struct sctp_chunk *chunk)
|
|||
* For now don't do anything for now.
|
||||
*/
|
||||
if (chunk->auth) {
|
||||
chunk->shkey = asoc->shkey;
|
||||
if (!chunk->shkey)
|
||||
chunk->shkey = asoc->shkey;
|
||||
sctp_auth_shkey_hold(chunk->shkey);
|
||||
}
|
||||
skb->sk = asoc ? asoc->base.sk : NULL;
|
||||
|
|
|
|||
|
|
@ -155,9 +155,24 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk)
|
|||
|
||||
static void sctp_set_owner_w_migrate(struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_set_owner_w(chunk);
|
||||
if (chunk->shkey)
|
||||
sctp_auth_shkey_release(chunk->shkey);
|
||||
struct sctp_shared_key *shkey = chunk->shkey;
|
||||
|
||||
if (chunk->msg)
|
||||
sctp_set_owner_w(chunk);
|
||||
else
|
||||
sctp_control_set_owner_w(chunk);
|
||||
|
||||
if (shkey)
|
||||
sctp_auth_shkey_release(shkey);
|
||||
}
|
||||
|
||||
static void sctp_process_tx_chunk(struct sctp_association *asoc,
|
||||
struct sctp_chunk *chunk, bool clear,
|
||||
void (*cb)(struct sctp_chunk *))
|
||||
{
|
||||
if ((clear && asoc->base.sk == chunk->skb->sk) ||
|
||||
(!clear && asoc->base.sk != chunk->skb->sk))
|
||||
cb(chunk);
|
||||
}
|
||||
|
||||
#define traverse_and_process() \
|
||||
|
|
@ -165,17 +180,14 @@ 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); \
|
||||
} \
|
||||
list_for_each_entry(c, &msg->chunks, frag_list) \
|
||||
sctp_process_tx_chunk(asoc, c, clear, cb); \
|
||||
prev_msg = msg; \
|
||||
} while (0)
|
||||
|
||||
static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
|
||||
bool clear,
|
||||
void (*cb)(struct sctp_chunk *))
|
||||
static void sctp_for_each_tx_chunk(struct sctp_association *asoc,
|
||||
bool clear,
|
||||
void (*cb)(struct sctp_chunk *))
|
||||
|
||||
{
|
||||
struct sctp_datamsg *msg, *prev_msg = NULL;
|
||||
|
|
@ -198,6 +210,21 @@ static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
|
|||
|
||||
list_for_each_entry(chunk, &q->out_chunk_list, list)
|
||||
traverse_and_process();
|
||||
|
||||
list_for_each_entry(chunk, &q->control_chunk_list, list)
|
||||
sctp_process_tx_chunk(asoc, chunk, clear, cb);
|
||||
|
||||
list_for_each_entry(chunk, &asoc->asconf_ack_list, transmitted_list)
|
||||
sctp_process_tx_chunk(asoc, chunk, clear, cb);
|
||||
|
||||
list_for_each_entry(chunk, &asoc->addip_chunk_list, list)
|
||||
sctp_process_tx_chunk(asoc, chunk, clear, cb);
|
||||
|
||||
if (asoc->strreset_chunk)
|
||||
sctp_process_tx_chunk(asoc, asoc->strreset_chunk, clear, cb);
|
||||
|
||||
if (asoc->addip_last_asconf)
|
||||
sctp_process_tx_chunk(asoc, asoc->addip_last_asconf, clear, cb);
|
||||
}
|
||||
|
||||
static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
|
||||
|
|
@ -9645,9 +9672,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, true, sctp_clear_owner_w);
|
||||
sctp_for_each_tx_chunk(assoc, true, sctp_clear_owner_w);
|
||||
sctp_assoc_migrate(assoc, newsk);
|
||||
sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w_migrate);
|
||||
sctp_for_each_tx_chunk(assoc, false, sctp_set_owner_w_migrate);
|
||||
|
||||
/* If the association on the newsk is already closed before accept()
|
||||
* is called, set RCV_SHUTDOWN flag.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user