From: Sona Sarmadi Date: Tue, 27 Jan 2015 13:04:07 +0000 (+0100) Subject: net-sctp: CVE-2014-3673, CVE-2014-3687, CVE-2014-3688 X-Git-Tag: 2.1~534^2~21 X-Git-Url: https://code.ossystems.io/gitweb?a=commitdiff_plain;h=26303c11e8502a997caee96a4b342fdf084bd4ab;p=meta-freescale.git net-sctp: CVE-2014-3673, CVE-2014-3687, CVE-2014-3688 CVE-2014-3673 skb_over_panic when receiving malformed ASCONF chunks Fixes: b896b82be4ae ("[SCTP] ADDIP: Support for processing incoming ASCONF_ACK chunks.") CVE-2014-3687 panic on duplicate ASCONF chunks Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") CVE-2014-3688 remote memory pressure from excessive queueing Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") References: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3673 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3687 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3688 http://www.openwall.com/lists/oss-security/2014/11/13/8 Signed-off-by: Sona Sarmadi --- diff --git a/meta-fsl-ppc/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch b/meta-fsl-ppc/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch new file mode 100644 index 00000000..68289f28 --- /dev/null +++ b/meta-fsl-ppc/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch @@ -0,0 +1,348 @@ +From bbd951a21e0fd555cd9ede44c7196af09d04d171 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 9 Oct 2014 22:55:31 +0200 +Subject: [PATCH] net: sctp: fix skb_over_panic when receiving malformed ASCONF + chunks + +commit 9de7922bc709eee2f609cd01d98aaedc4cf5ea74 upstream. + +Commit 6f4c618ddb0 ("SCTP : Add paramters validity check for +ASCONF chunk") added basic verification of ASCONF chunks, however, +it is still possible to remotely crash a server by sending a +special crafted ASCONF chunk, even up to pre 2.6.12 kernels: + +skb_over_panic: text:ffffffffa01ea1c3 len:31056 put:30768 + head:ffff88011bd81800 data:ffff88011bd81800 tail:0x7950 + end:0x440 dev: + ------------[ cut here ]------------ +kernel BUG at net/core/skbuff.c:129! +[...] +Call Trace: + + [] skb_put+0x5c/0x70 + [] sctp_addto_chunk+0x63/0xd0 [sctp] + [] sctp_process_asconf+0x1af/0x540 [sctp] + [] ? _read_unlock_bh+0x15/0x20 + [] sctp_sf_do_asconf+0x168/0x240 [sctp] + [] sctp_do_sm+0x71/0x1210 [sctp] + [] ? fib_rules_lookup+0xad/0xf0 + [] ? sctp_cmp_addr_exact+0x32/0x40 [sctp] + [] sctp_assoc_bh_rcv+0xd3/0x180 [sctp] + [] sctp_inq_push+0x56/0x80 [sctp] + [] sctp_rcv+0x982/0xa10 [sctp] + [] ? ipt_local_in_hook+0x23/0x28 [iptable_filter] + [] ? nf_iterate+0x69/0xb0 + [] ? ip_local_deliver_finish+0x0/0x2d0 + [] ? nf_hook_slow+0x76/0x120 + [] ? ip_local_deliver_finish+0x0/0x2d0 + [] ip_local_deliver_finish+0xdd/0x2d0 + [] ip_local_deliver+0x98/0xa0 + [] ip_rcv_finish+0x12d/0x440 + [] ip_rcv+0x275/0x350 + [] __netif_receive_skb+0x4ab/0x750 + [] netif_receive_skb+0x58/0x60 + +This can be triggered e.g., through a simple scripted nmap +connection scan injecting the chunk after the handshake, for +example, ... + + -------------- INIT[ASCONF; ASCONF_ACK] -------------> + <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------ + -------------------- COOKIE-ECHO --------------------> + <-------------------- COOKIE-ACK --------------------- + ------------------ ASCONF; UNKNOWN ------------------> + +... where ASCONF chunk of length 280 contains 2 parameters ... + + 1) Add IP address parameter (param length: 16) + 2) Add/del IP address parameter (param length: 255) + +... followed by an UNKNOWN chunk of e.g. 4 bytes. Here, the +Address Parameter in the ASCONF chunk is even missing, too. +This is just an example and similarly-crafted ASCONF chunks +could be used just as well. + +The ASCONF chunk passes through sctp_verify_asconf() as all +parameters passed sanity checks, and after walking, we ended +up successfully at the chunk end boundary, and thus may invoke +sctp_process_asconf(). Parameter walking is done with +WORD_ROUND() to take padding into account. + +In sctp_process_asconf()'s TLV processing, we may fail in +sctp_process_asconf_param() e.g., due to removal of the IP +address that is also the source address of the packet containing +the ASCONF chunk, and thus we need to add all TLVs after the +failure to our ASCONF response to remote via helper function +sctp_add_asconf_response(), which basically invokes a +sctp_addto_chunk() adding the error parameters to the given +skb. + +When walking to the next parameter this time, we proceed +with ... + + length = ntohs(asconf_param->param_hdr.length); + asconf_param = (void *)asconf_param + length; + +... instead of the WORD_ROUND()'ed length, thus resulting here +in an off-by-one that leads to reading the follow-up garbage +parameter length of 12336, and thus throwing an skb_over_panic +for the reply when trying to sctp_addto_chunk() next time, +which implicitly calls the skb_put() with that length. + +Fix it by using sctp_walk_params() [ which is also used in +INIT parameter processing ] macro in the verification *and* +in ASCONF processing: it will make sure we don't spill over, +that we walk parameters WORD_ROUND()'ed. Moreover, we're being +more defensive and guard against unknown parameter types and +missized addresses. + +Joint work with Vlad Yasevich. + +Fixes CVE-2014-3673 +Upstream-Status: Backport + +Fixes: b896b82be4ae ("[SCTP] ADDIP: Support for processing incoming ASCONF_ACK chunks.") +Signed-off-by: Daniel Borkmann +Signed-off-by: Vlad Yasevich +Acked-by: Neil Horman +Signed-off-by: David S. Miller +Cc: Josh Boyer +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + include/net/sctp/sm.h | 6 +-- + net/sctp/sm_make_chunk.c | 99 +++++++++++++++++++++++++++--------------------- + net/sctp/sm_statefuns.c | 18 +-------- + 3 files changed, 60 insertions(+), 63 deletions(-) + +diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h +index 4ef75af..c91b6f5 100644 +--- a/include/net/sctp/sm.h ++++ b/include/net/sctp/sm.h +@@ -249,9 +249,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, + int, __be16); + struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, + union sctp_addr *addr); +-int sctp_verify_asconf(const struct sctp_association *asoc, +- struct sctp_paramhdr *param_hdr, void *chunk_end, +- struct sctp_paramhdr **errp); ++bool sctp_verify_asconf(const struct sctp_association *asoc, ++ struct sctp_chunk *chunk, bool addr_param_needed, ++ struct sctp_paramhdr **errp); + struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, + struct sctp_chunk *asconf); + int sctp_process_asconf_ack(struct sctp_association *asoc, +diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c +index e342387..d800160 100644 +--- a/net/sctp/sm_make_chunk.c ++++ b/net/sctp/sm_make_chunk.c +@@ -3126,50 +3126,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, + return SCTP_ERROR_NO_ERROR; + } + +-/* Verify the ASCONF packet before we process it. */ +-int sctp_verify_asconf(const struct sctp_association *asoc, +- struct sctp_paramhdr *param_hdr, void *chunk_end, +- struct sctp_paramhdr **errp) { +- sctp_addip_param_t *asconf_param; ++/* Verify the ASCONF packet before we process it. */ ++bool sctp_verify_asconf(const struct sctp_association *asoc, ++ struct sctp_chunk *chunk, bool addr_param_needed, ++ struct sctp_paramhdr **errp) ++{ ++ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr; + union sctp_params param; +- int length, plen; +- +- param.v = (sctp_paramhdr_t *) param_hdr; +- while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) { +- length = ntohs(param.p->length); +- *errp = param.p; ++ bool addr_param_seen = false; + +- if (param.v > chunk_end - length || +- length < sizeof(sctp_paramhdr_t)) +- return 0; ++ sctp_walk_params(param, addip, addip_hdr.params) { ++ size_t length = ntohs(param.p->length); + ++ *errp = param.p; + switch (param.p->type) { ++ case SCTP_PARAM_ERR_CAUSE: ++ break; ++ case SCTP_PARAM_IPV4_ADDRESS: ++ if (length != sizeof(sctp_ipv4addr_param_t)) ++ return false; ++ addr_param_seen = true; ++ break; ++ case SCTP_PARAM_IPV6_ADDRESS: ++ if (length != sizeof(sctp_ipv6addr_param_t)) ++ return false; ++ addr_param_seen = true; ++ break; + case SCTP_PARAM_ADD_IP: + case SCTP_PARAM_DEL_IP: + case SCTP_PARAM_SET_PRIMARY: +- asconf_param = (sctp_addip_param_t *)param.v; +- plen = ntohs(asconf_param->param_hdr.length); +- if (plen < sizeof(sctp_addip_param_t) + +- sizeof(sctp_paramhdr_t)) +- return 0; ++ /* In ASCONF chunks, these need to be first. */ ++ if (addr_param_needed && !addr_param_seen) ++ return false; ++ length = ntohs(param.addip->param_hdr.length); ++ if (length < sizeof(sctp_addip_param_t) + ++ sizeof(sctp_paramhdr_t)) ++ return false; + break; + case SCTP_PARAM_SUCCESS_REPORT: + case SCTP_PARAM_ADAPTATION_LAYER_IND: + if (length != sizeof(sctp_addip_param_t)) +- return 0; +- ++ return false; + break; + default: +- break; ++ /* This is unkown to us, reject! */ ++ return false; + } +- +- param.v += WORD_ROUND(length); + } + +- if (param.v != chunk_end) +- return 0; ++ /* Remaining sanity checks. */ ++ if (addr_param_needed && !addr_param_seen) ++ return false; ++ if (!addr_param_needed && addr_param_seen) ++ return false; ++ if (param.v != chunk->chunk_end) ++ return false; + +- return 1; ++ return true; + } + + /* Process an incoming ASCONF chunk with the next expected serial no. and +@@ -3178,16 +3191,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc, + struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, + struct sctp_chunk *asconf) + { ++ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr; ++ bool all_param_pass = true; ++ union sctp_params param; + sctp_addiphdr_t *hdr; + union sctp_addr_param *addr_param; + sctp_addip_param_t *asconf_param; + struct sctp_chunk *asconf_ack; +- + __be16 err_code; + int length = 0; + int chunk_len; + __u32 serial; +- int all_param_pass = 1; + + chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); + hdr = (sctp_addiphdr_t *)asconf->skb->data; +@@ -3215,9 +3229,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, + goto done; + + /* Process the TLVs contained within the ASCONF chunk. */ +- while (chunk_len > 0) { ++ sctp_walk_params(param, addip, addip_hdr.params) { ++ /* Skip preceeding address parameters. */ ++ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS || ++ param.p->type == SCTP_PARAM_IPV6_ADDRESS) ++ continue; ++ + err_code = sctp_process_asconf_param(asoc, asconf, +- asconf_param); ++ param.addip); + /* ADDIP 4.1 A7) + * If an error response is received for a TLV parameter, + * all TLVs with no response before the failed TLV are +@@ -3225,28 +3244,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, + * the failed response are considered unsuccessful unless + * a specific success indication is present for the parameter. + */ +- if (SCTP_ERROR_NO_ERROR != err_code) +- all_param_pass = 0; +- ++ if (err_code != SCTP_ERROR_NO_ERROR) ++ all_param_pass = false; + if (!all_param_pass) +- sctp_add_asconf_response(asconf_ack, +- asconf_param->crr_id, err_code, +- asconf_param); ++ sctp_add_asconf_response(asconf_ack, param.addip->crr_id, ++ err_code, param.addip); + + /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add + * an IP address sends an 'Out of Resource' in its response, it + * MUST also fail any subsequent add or delete requests bundled + * in the ASCONF. + */ +- if (SCTP_ERROR_RSRC_LOW == err_code) ++ if (err_code == SCTP_ERROR_RSRC_LOW) + goto done; +- +- /* Move to the next ASCONF param. */ +- length = ntohs(asconf_param->param_hdr.length); +- asconf_param = (void *)asconf_param + length; +- chunk_len -= length; + } +- + done: + asoc->peer.addip_serial++; + +diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c +index 62623cc..bf12098 100644 +--- a/net/sctp/sm_statefuns.c ++++ b/net/sctp/sm_statefuns.c +@@ -3595,9 +3595,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, + struct sctp_chunk *asconf_ack = NULL; + struct sctp_paramhdr *err_param = NULL; + sctp_addiphdr_t *hdr; +- union sctp_addr_param *addr_param; + __u32 serial; +- int length; + + if (!sctp_vtag_verify(chunk, asoc)) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, +@@ -3622,17 +3620,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, + hdr = (sctp_addiphdr_t *)chunk->skb->data; + serial = ntohl(hdr->serial); + +- addr_param = (union sctp_addr_param *)hdr->params; +- length = ntohs(addr_param->p.length); +- if (length < sizeof(sctp_paramhdr_t)) +- return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, +- (void *)addr_param, commands); +- + /* Verify the ASCONF chunk before processing it. */ +- if (!sctp_verify_asconf(asoc, +- (sctp_paramhdr_t *)((void *)addr_param + length), +- (void *)chunk->chunk_end, +- &err_param)) ++ if (!sctp_verify_asconf(asoc, chunk, true, &err_param)) + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, + (void *)err_param, commands); + +@@ -3750,10 +3739,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, + rcvd_serial = ntohl(addip_hdr->serial); + + /* Verify the ASCONF-ACK chunk before processing it. */ +- if (!sctp_verify_asconf(asoc, +- (sctp_paramhdr_t *)addip_hdr->params, +- (void *)asconf_ack->chunk_end, +- &err_param)) ++ if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param)) + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, + (void *)err_param, commands); + +-- +1.9.1 + diff --git a/meta-fsl-ppc/recipes-kernel/linux/files/0002-net-sctp-CVE-2014-3687.patch b/meta-fsl-ppc/recipes-kernel/linux/files/0002-net-sctp-CVE-2014-3687.patch new file mode 100644 index 00000000..b05aaf2b --- /dev/null +++ b/meta-fsl-ppc/recipes-kernel/linux/files/0002-net-sctp-CVE-2014-3687.patch @@ -0,0 +1,102 @@ +From a723db0be941b8aebaa1a98b33d17a91b16603e4 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 9 Oct 2014 22:55:32 +0200 +Subject: [PATCH] net: sctp: fix panic on duplicate ASCONF chunks + +commit b69040d8e39f20d5215a03502a8e8b4c6ab78395 upstream. + +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 CVE-2014-3687 +Upstream-Status: Backport + +Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") +Signed-off-by: Daniel Borkmann +Signed-off-by: Vlad Yasevich +Signed-off-by: David S. Miller +Cc: Josh Boyer +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + include/net/sctp/sctp.h | 5 +++++ + net/sctp/associola.c | 2 ++ + 2 files changed, 7 insertions(+) + +diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h +index 3794c5a..3848934 100644 +--- a/include/net/sctp/sctp.h ++++ b/include/net/sctp/sctp.h +@@ -454,6 +454,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat + asoc->pmtu_pending = 0; + } + ++static inline bool sctp_chunk_pending(const struct sctp_chunk *chunk) ++{ ++ return !list_empty(&chunk->list); ++} ++ + /* Walk through a list of TLV parameters. Don't trust the + * individual parameter lengths and instead depend on + * the chunk length to indicate when to stop. Make sure +diff --git a/net/sctp/associola.c b/net/sctp/associola.c +index ad5cd6f..737050f 100644 +--- a/net/sctp/associola.c ++++ b/net/sctp/associola.c +@@ -1645,6 +1645,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack( + * ack chunk whose serial number matches that of the request. + */ + list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) { ++ if (sctp_chunk_pending(ack)) ++ continue; + if (ack->subh.addip_hdr->serial == serial) { + sctp_chunk_hold(ack); + return ack; +-- +1.9.1 + diff --git a/meta-fsl-ppc/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch b/meta-fsl-ppc/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch new file mode 100644 index 00000000..1b4716d0 --- /dev/null +++ b/meta-fsl-ppc/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch @@ -0,0 +1,160 @@ +From e476841415c1b7b54e4118d8a219f5db71878675 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 9 Oct 2014 22:55:33 +0200 +Subject: [PATCH] net: sctp: fix remote memory pressure from excessive queueing + +commit 26b87c7881006311828bb0ab271a551a62dcceb4 upstream. + +This scenario is not limited to ASCONF, just taken as one +example triggering the issue. When receiving ASCONF probes +in the form of ... + + -------------- INIT[ASCONF; ASCONF_ACK] -------------> + <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------ + -------------------- COOKIE-ECHO --------------------> + <-------------------- COOKIE-ACK --------------------- + ---- ASCONF_a; [ASCONF_b; ...; ASCONF_n;] JUNK ------> + [...] + ---- ASCONF_m; [ASCONF_o; ...; ASCONF_z;] JUNK ------> + +... where ASCONF_a, ASCONF_b, ..., ASCONF_z are good-formed +ASCONFs and have increasing serial numbers, we process such +ASCONF chunk(s) marked with !end_of_packet and !singleton, +since we have not yet reached the SCTP packet end. SCTP does +only do verification on a chunk by chunk basis, as an SCTP +packet is nothing more than just a container of a stream of +chunks which it eats up one by one. + +We could run into the case that we receive a packet with a +malformed tail, above marked as trailing JUNK. All previous +chunks are here goodformed, so the stack will eat up all +previous chunks up to this point. In case JUNK does not fit +into a chunk header and there are no more other chunks in +the input queue, or in case JUNK contains a garbage chunk +header, but the encoded chunk length would exceed the skb +tail, or we came here from an entirely different scenario +and the chunk has pdiscard=1 mark (without having had a flush +point), it will happen, that we will excessively queue up +the association's output queue (a correct final chunk may +then turn it into a response flood when flushing the +queue ;)): I ran a simple script with incremental ASCONF +serial numbers and could see the server side consuming +excessive amount of RAM [before/after: up to 2GB and more]. + +The issue at heart is that the chunk train basically ends +with !end_of_packet and !singleton markers and since commit +2e3216cd54b1 ("sctp: Follow security requirement of responding +with 1 packet") therefore preventing an output queue flush +point in sctp_do_sm() -> sctp_cmd_interpreter() on the input +chunk (chunk = event_arg) even though local_cork is set, +but its precedence has changed since then. In the normal +case, the last chunk with end_of_packet=1 would trigger the +queue flush to accommodate possible outgoing bundling. + +In the input queue, sctp_inq_pop() seems to do the right thing +in terms of discarding invalid chunks. So, above JUNK will +not enter the state machine and instead be released and exit +the sctp_assoc_bh_rcv() chunk processing loop. It's simply +the flush point being missing at loop exit. Adding a try-flush +approach on the output queue might not work as the underlying +infrastructure might be long gone at this point due to the +side-effect interpreter run. + +One possibility, albeit a bit of a kludge, would be to defer +invalid chunk freeing into the state machine in order to +possibly trigger packet discards and thus indirectly a queue +flush on error. It would surely be better to discard chunks +as in the current, perhaps better controlled environment, but +going back and forth, it's simply architecturally not possible. +I tried various trailing JUNK attack cases and it seems to +look good now. + +Joint work with Vlad Yasevich. + +Fixes CVE-2014-3688 +Upstream-Status: Backport + +Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") +Signed-off-by: Daniel Borkmann +Signed-off-by: Vlad Yasevich +Signed-off-by: David S. Miller +Cc: Josh Boyer +Signed-off-by: Jiri Slaby +Signed-off-by: Sona Sarmadi +--- + net/sctp/inqueue.c | 33 +++++++-------------------------- + net/sctp/sm_statefuns.c | 3 +++ + 2 files changed, 10 insertions(+), 26 deletions(-) + +diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c +index 5856932..560cd41 100644 +--- a/net/sctp/inqueue.c ++++ b/net/sctp/inqueue.c +@@ -141,18 +141,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) + } else { + /* Nothing to do. Next chunk in the packet, please. */ + ch = (sctp_chunkhdr_t *) chunk->chunk_end; +- + /* Force chunk->skb->data to chunk->chunk_end. */ +- skb_pull(chunk->skb, +- chunk->chunk_end - chunk->skb->data); +- +- /* Verify that we have at least chunk headers +- * worth of buffer left. +- */ +- if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) { +- sctp_chunk_free(chunk); +- chunk = queue->in_progress = NULL; +- } ++ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data); ++ /* We are guaranteed to pull a SCTP header. */ + } + } + +@@ -188,24 +179,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) + skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); + chunk->subh.v = NULL; /* Subheader is no longer valid. */ + +- if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) { ++ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) < ++ skb_tail_pointer(chunk->skb)) { + /* This is not a singleton */ + chunk->singleton = 0; + } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) { +- /* RFC 2960, Section 6.10 Bundling +- * +- * Partial chunks MUST NOT be placed in an SCTP packet. +- * If the receiver detects a partial chunk, it MUST drop +- * the chunk. +- * +- * Since the end of the chunk is past the end of our buffer +- * (which contains the whole packet, we can freely discard +- * the whole packet. +- */ +- sctp_chunk_free(chunk); +- chunk = queue->in_progress = NULL; +- +- return NULL; ++ /* Discard inside state machine. */ ++ chunk->pdiscard = 1; ++ chunk->chunk_end = skb_tail_pointer(chunk->skb); + } else { + /* We are at the end of the packet, so mark the chunk + * in case we need to send a SACK. +diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c +index 1dbcc6a..62623cc 100644 +--- a/net/sctp/sm_statefuns.c ++++ b/net/sctp/sm_statefuns.c +@@ -171,6 +171,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk, + { + __u16 chunk_length = ntohs(chunk->chunk_hdr->length); + ++ /* Previously already marked? */ ++ if (unlikely(chunk->pdiscard)) ++ return 0; + if (unlikely(chunk_length < required_length)) + return 0; + +-- +1.9.1 + diff --git a/meta-fsl-ppc/recipes-kernel/linux/linux-qoriq_3.12.bb b/meta-fsl-ppc/recipes-kernel/linux/linux-qoriq_3.12.bb index 5993b59a..96656322 100644 --- a/meta-fsl-ppc/recipes-kernel/linux/linux-qoriq_3.12.bb +++ b/meta-fsl-ppc/recipes-kernel/linux/linux-qoriq_3.12.bb @@ -19,6 +19,9 @@ SRC_URI = "git://git.freescale.com/ppc/sdk/linux.git;nobranch=1 \ file://0004-USB-CVE-2014-3185.patch \ file://0001-kvm-iommu-CVE-2014-3601.patch \ file://0002-kvm-iommu-CVE-2014-8369.patch \ + file://0001-net-sctp-CVE-2014-3673.patch \ + file://0002-net-sctp-CVE-2014-3687.patch \ + file://0003-net-sctp-CVE-2014-3688.patch \ " SRCREV = "6619b8b55796cdf0cec04b66a71288edd3057229"