diff options
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.patch | 160 |
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 @@ | |||
1 | From e476841415c1b7b54e4118d8a219f5db71878675 Mon Sep 17 00:00:00 2001 | ||
2 | From: Daniel Borkmann <dborkman@redhat.com> | ||
3 | Date: Thu, 9 Oct 2014 22:55:33 +0200 | ||
4 | Subject: [PATCH] net: sctp: fix remote memory pressure from excessive queueing | ||
5 | |||
6 | commit 26b87c7881006311828bb0ab271a551a62dcceb4 upstream. | ||
7 | |||
8 | This scenario is not limited to ASCONF, just taken as one | ||
9 | example triggering the issue. When receiving ASCONF probes | ||
10 | in 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 | ||
21 | ASCONFs and have increasing serial numbers, we process such | ||
22 | ASCONF chunk(s) marked with !end_of_packet and !singleton, | ||
23 | since we have not yet reached the SCTP packet end. SCTP does | ||
24 | only do verification on a chunk by chunk basis, as an SCTP | ||
25 | packet is nothing more than just a container of a stream of | ||
26 | chunks which it eats up one by one. | ||
27 | |||
28 | We could run into the case that we receive a packet with a | ||
29 | malformed tail, above marked as trailing JUNK. All previous | ||
30 | chunks are here goodformed, so the stack will eat up all | ||
31 | previous chunks up to this point. In case JUNK does not fit | ||
32 | into a chunk header and there are no more other chunks in | ||
33 | the input queue, or in case JUNK contains a garbage chunk | ||
34 | header, but the encoded chunk length would exceed the skb | ||
35 | tail, or we came here from an entirely different scenario | ||
36 | and the chunk has pdiscard=1 mark (without having had a flush | ||
37 | point), it will happen, that we will excessively queue up | ||
38 | the association's output queue (a correct final chunk may | ||
39 | then turn it into a response flood when flushing the | ||
40 | queue ;)): I ran a simple script with incremental ASCONF | ||
41 | serial numbers and could see the server side consuming | ||
42 | excessive amount of RAM [before/after: up to 2GB and more]. | ||
43 | |||
44 | The issue at heart is that the chunk train basically ends | ||
45 | with !end_of_packet and !singleton markers and since commit | ||
46 | 2e3216cd54b1 ("sctp: Follow security requirement of responding | ||
47 | with 1 packet") therefore preventing an output queue flush | ||
48 | point in sctp_do_sm() -> sctp_cmd_interpreter() on the input | ||
49 | chunk (chunk = event_arg) even though local_cork is set, | ||
50 | but its precedence has changed since then. In the normal | ||
51 | case, the last chunk with end_of_packet=1 would trigger the | ||
52 | queue flush to accommodate possible outgoing bundling. | ||
53 | |||
54 | In the input queue, sctp_inq_pop() seems to do the right thing | ||
55 | in terms of discarding invalid chunks. So, above JUNK will | ||
56 | not enter the state machine and instead be released and exit | ||
57 | the sctp_assoc_bh_rcv() chunk processing loop. It's simply | ||
58 | the flush point being missing at loop exit. Adding a try-flush | ||
59 | approach on the output queue might not work as the underlying | ||
60 | infrastructure might be long gone at this point due to the | ||
61 | side-effect interpreter run. | ||
62 | |||
63 | One possibility, albeit a bit of a kludge, would be to defer | ||
64 | invalid chunk freeing into the state machine in order to | ||
65 | possibly trigger packet discards and thus indirectly a queue | ||
66 | flush on error. It would surely be better to discard chunks | ||
67 | as in the current, perhaps better controlled environment, but | ||
68 | going back and forth, it's simply architecturally not possible. | ||
69 | I tried various trailing JUNK attack cases and it seems to | ||
70 | look good now. | ||
71 | |||
72 | Joint work with Vlad Yasevich. | ||
73 | |||
74 | Fixes CVE-2014-3688 | ||
75 | Upstream-Status: Backport | ||
76 | |||
77 | Fixes: 2e3216cd54b1 ("sctp: Follow security requirement of responding with 1 packet") | ||
78 | Signed-off-by: Daniel Borkmann <dborkman@redhat.com> | ||
79 | Signed-off-by: Vlad Yasevich <vyasevich@gmail.com> | ||
80 | Signed-off-by: David S. Miller <davem@davemloft.net> | ||
81 | Cc: Josh Boyer <jwboyer@fedoraproject.org> | ||
82 | Signed-off-by: Jiri Slaby <jslaby@suse.cz> | ||
83 | Signed-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 | |||
89 | diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c | ||
90 | index 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. | ||
144 | diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c | ||
145 | index 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 | -- | ||
159 | 1.9.1 | ||
160 | |||