aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2014-10-09 16:55:32 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-14 12:46:22 -0400
commitb69040d8e39f20d5215a03502a8e8b4c6ab78395 (patch)
treea3367d545be53156bce834449c6698b01ae0a80a /net/sctp
parent9de7922bc709eee2f609cd01d98aaedc4cf5ea74 (diff)
net: sctp: fix panic on duplicate ASCONF chunks
When receiving a e.g. semi-good formed connection scan in the form of ... -------------- INIT[ASCONF; ASCONF_ACK] -------------> <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------ -------------------- COOKIE-ECHO --------------------> <-------------------- COOKIE-ACK --------------------- ---------------- ASCONF_a; ASCONF_b -----------------> ... where ASCONF_a equals ASCONF_b chunk (at least both serials need to be equal), we panic an SCTP server! The problem is that good-formed ASCONF chunks that we reply with ASCONF_ACK chunks are cached per serial. Thus, when we receive a same ASCONF chunk twice (e.g. through a lost ASCONF_ACK), we do not need to process them again on the server side (that was the idea, also proposed in the RFC). Instead, we know it was cached and we just resend the cached chunk instead. So far, so good. Where things get nasty is in SCTP's side effect interpreter, that is, sctp_cmd_interpreter(): While incoming ASCONF_a (chunk = event_arg) is being marked !end_of_packet and !singleton, and we have an association context, we do not flush the outqueue the first time after processing the ASCONF_ACK singleton chunk via SCTP_CMD_REPLY. Instead, we keep it queued up, although we set local_cork to 1. Commit 2e3216cd54b1 changed the precedence, so that as long as we get bundled, incoming chunks we try possible bundling on outgoing queue as well. Before this commit, we would just flush the output queue. Now, while ASCONF_a's ASCONF_ACK sits in the corked outq, we continue to process the same ASCONF_b chunk from the packet. As we have cached the previous ASCONF_ACK, we find it, grab it and do another SCTP_CMD_REPLY command on it. So, effectively, we rip the chunk->list pointers and requeue the same ASCONF_ACK chunk another time. Since we process ASCONF_b, it's correctly marked with end_of_packet and we enforce an uncork, and thus flush, thus crashing the kernel. Fix it by testing if the ASCONF_ACK is currently pending and if that is the case, do not requeue it. When flushing the output queue we may relink the chunk for preparing an outgoing packet, but eventually unlink it when it's copied into the skb right before transmission. Joint work with Vlad Yasevich. Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index a88b8524846e..f791edd64d6c 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1668,6 +1668,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
1668 * ack chunk whose serial number matches that of the request. 1668 * ack chunk whose serial number matches that of the request.
1669 */ 1669 */
1670 list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) { 1670 list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
1671 if (sctp_chunk_pending(ack))
1672 continue;
1671 if (ack->subh.addip_hdr->serial == serial) { 1673 if (ack->subh.addip_hdr->serial == serial) {
1672 sctp_chunk_hold(ack); 1674 sctp_chunk_hold(ack);
1673 return ack; 1675 return ack;