summaryrefslogtreecommitdiffstats
path: root/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch')
-rw-r--r--recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch160
1 files changed, 160 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch b/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch
new file mode 100644
index 0000000..1b4716d
--- /dev/null
+++ b/recipes-kernel/linux/files/0003-net-sctp-CVE-2014-3688.patch
@@ -0,0 +1,160 @@
1From e476841415c1b7b54e4118d8a219f5db71878675 Mon Sep 17 00:00:00 2001
2From: Daniel Borkmann <dborkman@redhat.com>
3Date: Thu, 9 Oct 2014 22:55:33 +0200
4Subject: [PATCH] net: sctp: fix remote memory pressure from excessive queueing
5
6commit 26b87c7881006311828bb0ab271a551a62dcceb4 upstream.
7
8This scenario is not limited to ASCONF, just taken as one
9example triggering the issue. When receiving ASCONF probes
10in the form of ...
11
12 -------------- INIT[ASCONF; ASCONF_ACK] ------------->
13 <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------
14 -------------------- COOKIE-ECHO -------------------->
15 <-------------------- COOKIE-ACK ---------------------
16 ---- ASCONF_a; [ASCONF_b; ...; ASCONF_n;] JUNK ------>
17 [...]
18 ---- ASCONF_m; [ASCONF_o; ...; ASCONF_z;] JUNK ------>
19
20... where ASCONF_a, ASCONF_b, ..., ASCONF_z are good-formed
21ASCONFs and have increasing serial numbers, we process such
22ASCONF chunk(s) marked with !end_of_packet and !singleton,
23since we have not yet reached the SCTP packet end. SCTP does
24only do verification on a chunk by chunk basis, as an SCTP
25packet is nothing more than just a container of a stream of
26chunks which it eats up one by one.
27
28We could run into the case that we receive a packet with a
29malformed tail, above marked as trailing JUNK. All previous
30chunks are here goodformed, so the stack will eat up all
31previous chunks up to this point. In case JUNK does not fit
32into a chunk header and there are no more other chunks in
33the input queue, or in case JUNK contains a garbage chunk
34header, but the encoded chunk length would exceed the skb
35tail, or we came here from an entirely different scenario
36and the chunk has pdiscard=1 mark (without having had a flush
37point), it will happen, that we will excessively queue up
38the association's output queue (a correct final chunk may
39then turn it into a response flood when flushing the
40queue ;)): I ran a simple script with incremental ASCONF
41serial numbers and could see the server side consuming
42excessive amount of RAM [before/after: up to 2GB and more].
43
44The issue at heart is that the chunk train basically ends
45with !end_of_packet and !singleton markers and since commit
462e3216cd54b1 ("sctp: Follow security requirement of responding
47with 1 packet") therefore preventing an output queue flush
48point in sctp_do_sm() -> sctp_cmd_interpreter() on the input
49chunk (chunk = event_arg) even though local_cork is set,
50but its precedence has changed since then. In the normal
51case, the last chunk with end_of_packet=1 would trigger the
52queue flush to accommodate possible outgoing bundling.
53
54In the input queue, sctp_inq_pop() seems to do the right thing
55in terms of discarding invalid chunks. So, above JUNK will
56not enter the state machine and instead be released and exit
57the sctp_assoc_bh_rcv() chunk processing loop. It's simply
58the flush point being missing at loop exit. Adding a try-flush
59approach on the output queue might not work as the underlying
60infrastructure might be long gone at this point due to the
61side-effect interpreter run.
62
63One possibility, albeit a bit of a kludge, would be to defer
64invalid chunk freeing into the state machine in order to
65possibly trigger packet discards and thus indirectly a queue
66flush on error. It would surely be better to discard chunks
67as in the current, perhaps better controlled environment, but
68going back and forth, it's simply architecturally not possible.
69I tried various trailing JUNK attack cases and it seems to
70look good now.
71
72Joint work with Vlad Yasevich.
73
74Fixes CVE-2014-3688
75Upstream-Status: Backport
76
77Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet")
78Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
79Signed-off-by: Vlad Yasevich <vyasevich@gmail.com>
80Signed-off-by: David S. Miller <davem@davemloft.net>
81Cc: Josh Boyer <jwboyer@fedoraproject.org>
82Signed-off-by: Jiri Slaby <jslaby@suse.cz>
83Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com>
84---
85 net/sctp/inqueue.c | 33 +++++++--------------------------
86 net/sctp/sm_statefuns.c | 3 +++
87 2 files changed, 10 insertions(+), 26 deletions(-)
88
89diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
90index 5856932..560cd41 100644
91--- a/net/sctp/inqueue.c
92+++ b/net/sctp/inqueue.c
93@@ -141,18 +141,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
94 } else {
95 /* Nothing to do. Next chunk in the packet, please. */
96 ch = (sctp_chunkhdr_t *) chunk->chunk_end;
97-
98 /* Force chunk->skb->data to chunk->chunk_end. */
99- skb_pull(chunk->skb,
100- chunk->chunk_end - chunk->skb->data);
101-
102- /* Verify that we have at least chunk headers
103- * worth of buffer left.
104- */
105- if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) {
106- sctp_chunk_free(chunk);
107- chunk = queue->in_progress = NULL;
108- }
109+ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
110+ /* We are guaranteed to pull a SCTP header. */
111 }
112 }
113
114@@ -188,24 +179,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
115 skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
116 chunk->subh.v = NULL; /* Subheader is no longer valid. */
117
118- if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) {
119+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
120+ skb_tail_pointer(chunk->skb)) {
121 /* This is not a singleton */
122 chunk->singleton = 0;
123 } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
124- /* RFC 2960, Section 6.10 Bundling
125- *
126- * Partial chunks MUST NOT be placed in an SCTP packet.
127- * If the receiver detects a partial chunk, it MUST drop
128- * the chunk.
129- *
130- * Since the end of the chunk is past the end of our buffer
131- * (which contains the whole packet, we can freely discard
132- * the whole packet.
133- */
134- sctp_chunk_free(chunk);
135- chunk = queue->in_progress = NULL;
136-
137- return NULL;
138+ /* Discard inside state machine. */
139+ chunk->pdiscard = 1;
140+ chunk->chunk_end = skb_tail_pointer(chunk->skb);
141 } else {
142 /* We are at the end of the packet, so mark the chunk
143 * in case we need to send a SACK.
144diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
145index 1dbcc6a..62623cc 100644
146--- a/net/sctp/sm_statefuns.c
147+++ b/net/sctp/sm_statefuns.c
148@@ -171,6 +171,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
149 {
150 __u16 chunk_length = ntohs(chunk->chunk_hdr->length);
151
152+ /* Previously already marked? */
153+ if (unlikely(chunk->pdiscard))
154+ return 0;
155 if (unlikely(chunk_length < required_length))
156 return 0;
157
158--
1591.9.1
160