This patch should be applied to stable versions *only* before Linux 6.16, since DCCP implementation is removed in Linux 6.16.
Affected versions include: - 3.1-3.19 - 4.0-4.20 - 5.0-5.19 - 6.0-6.15
DCCP sockets in DCCP_REQUESTING state do not check the sequence number or acknowledgment number for incoming Reset, CloseReq, and Close packets.
As a result, an attacker can send a spoofed Reset packet while the client is in the requesting state. The client will accept the packet without verification and immediately close the connection, causing a denial of service (DoS) attack.
This patch moves the processing of Reset, Close, and CloseReq packets into dccp_rcv_request_sent_state_process() and validates the ack number before accepting them.
We tested it on Ubuntu 24.04 LTS (Linux 6.8) and it worked as expected.
Fixes: c0c2015056d7b ("dccp: Clean up slow-path input processing") Signed-off-by: Yizhou Zhao zhaoyz24@mails.tsinghua.edu.cn Cc: stable@vger.kernel.org --- net/dccp/input.c | 54 ++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 22 deletions(-)
diff --git a/net/dccp/input.c b/net/dccp/input.c index 2cbb757a8..0b1ffb044 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -397,21 +397,22 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, * / * Response processing continues in Step 10; Reset * processing continues in Step 9 * / */ + struct dccp_sock *dp = dccp_sk(sk); + + if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, + dp->dccps_awl, dp->dccps_awh)) { + dccp_pr_debug("invalid ackno: S.AWL=%llu, " + "P.ackno=%llu, S.AWH=%llu\n", + (unsigned long long)dp->dccps_awl, + (unsigned long long)DCCP_SKB_CB(skb)->dccpd_ack_seq, + (unsigned long long)dp->dccps_awh); + goto out_invalid_packet; + } + if (dh->dccph_type == DCCP_PKT_RESPONSE) { const struct inet_connection_sock *icsk = inet_csk(sk); - struct dccp_sock *dp = dccp_sk(sk); - long tstamp = dccp_timestamp(); - - if (!between48(DCCP_SKB_CB(skb)->dccpd_ack_seq, - dp->dccps_awl, dp->dccps_awh)) { - dccp_pr_debug("invalid ackno: S.AWL=%llu, " - "P.ackno=%llu, S.AWH=%llu\n", - (unsigned long long)dp->dccps_awl, - (unsigned long long)DCCP_SKB_CB(skb)->dccpd_ack_seq, - (unsigned long long)dp->dccps_awh); - goto out_invalid_packet; - }
+ long tstamp = dccp_timestamp(); /* * If option processing (Step 8) failed, return 1 here so that * dccp_v4_do_rcv() sends a Reset. The Reset code depends on @@ -496,6 +497,13 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk, } dccp_send_ack(sk); return -1; + } else if (dh->dccph_type == DCCP_PKT_RESET) { + dccp_rcv_reset(sk, skb); + return 0; + } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { + return dccp_rcv_closereq(sk, skb); + } else if (dh->dccph_type == DCCP_PKT_CLOSE) { + return dccp_rcv_close(sk, skb); }
out_invalid_packet: @@ -658,17 +666,19 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, * Set TIMEWAIT timer * Drop packet and return */ - if (dh->dccph_type == DCCP_PKT_RESET) { - dccp_rcv_reset(sk, skb); - return 0; - } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { /* Step 13 */ - if (dccp_rcv_closereq(sk, skb)) - return 0; - goto discard; - } else if (dh->dccph_type == DCCP_PKT_CLOSE) { /* Step 14 */ - if (dccp_rcv_close(sk, skb)) + if (sk->sk_state != DCCP_REQUESTING) { + if (dh->dccph_type == DCCP_PKT_RESET) { + dccp_rcv_reset(sk, skb); return 0; - goto discard; + } else if (dh->dccph_type == DCCP_PKT_CLOSEREQ) { /* Step 13 */ + if (dccp_rcv_closereq(sk, skb)) + return 0; + goto discard; + } else if (dh->dccph_type == DCCP_PKT_CLOSE) { /* Step 14 */ + if (dccp_rcv_close(sk, skb)) + return 0; + goto discard; + } }
switch (sk->sk_state) {