diff options
Diffstat (limited to 'recipes-kernel')
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon/net-unix-CVE-2013-7446.patch | 338 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon_3.19.bb | 1 | ||||
-rw-r--r-- | recipes-kernel/linux/linux-hierofalcon_4.1.bb | 1 |
3 files changed, 340 insertions, 0 deletions
diff --git a/recipes-kernel/linux/linux-hierofalcon/net-unix-CVE-2013-7446.patch b/recipes-kernel/linux/linux-hierofalcon/net-unix-CVE-2013-7446.patch new file mode 100644 index 0000000..e4e7c3c --- /dev/null +++ b/recipes-kernel/linux/linux-hierofalcon/net-unix-CVE-2013-7446.patch | |||
@@ -0,0 +1,338 @@ | |||
1 | From 5c77e26862ce604edea05b3442ed765e9756fe0f Mon Sep 17 00:00:00 2001 | ||
2 | From: Rainer Weikusat <rweikusat@mobileactivedefense.com> | ||
3 | Date: Fri, 20 Nov 2015 22:07:23 +0000 | ||
4 | Subject: unix: avoid use-after-free in ep_remove_wait_queue | ||
5 | |||
6 | [ Upstream commit 7d267278a9ece963d77eefec61630223fce08c6c ] | ||
7 | |||
8 | Rainer Weikusat <rweikusat@mobileactivedefense.com> writes: | ||
9 | An AF_UNIX datagram socket being the client in an n:1 association with | ||
10 | some server socket is only allowed to send messages to the server if the | ||
11 | receive queue of this socket contains at most sk_max_ack_backlog | ||
12 | datagrams. This implies that prospective writers might be forced to go | ||
13 | to sleep despite none of the message presently enqueued on the server | ||
14 | receive queue were sent by them. In order to ensure that these will be | ||
15 | woken up once space becomes again available, the present unix_dgram_poll | ||
16 | routine does a second sock_poll_wait call with the peer_wait wait queue | ||
17 | of the server socket as queue argument (unix_dgram_recvmsg does a wake | ||
18 | up on this queue after a datagram was received). This is inherently | ||
19 | problematic because the server socket is only guaranteed to remain alive | ||
20 | for as long as the client still holds a reference to it. In case the | ||
21 | connection is dissolved via connect or by the dead peer detection logic | ||
22 | in unix_dgram_sendmsg, the server socket may be freed despite "the | ||
23 | polling mechanism" (in particular, epoll) still has a pointer to the | ||
24 | corresponding peer_wait queue. There's no way to forcibly deregister a | ||
25 | wait queue with epoll. | ||
26 | |||
27 | Based on an idea by Jason Baron, the patch below changes the code such | ||
28 | that a wait_queue_t belonging to the client socket is enqueued on the | ||
29 | peer_wait queue of the server whenever the peer receive queue full | ||
30 | condition is detected by either a sendmsg or a poll. A wake up on the | ||
31 | peer queue is then relayed to the ordinary wait queue of the client | ||
32 | socket via wake function. The connection to the peer wait queue is again | ||
33 | dissolved if either a wake up is about to be relayed or the client | ||
34 | socket reconnects or a dead peer is detected or the client socket is | ||
35 | itself closed. This enables removing the second sock_poll_wait from | ||
36 | unix_dgram_poll, thus avoiding the use-after-free, while still ensuring | ||
37 | that no blocked writer sleeps forever. | ||
38 | |||
39 | CVE: CVE-2013-7446. | ||
40 | Upstream-Status: Backport | ||
41 | |||
42 | Signed-off-by: Rainer Weikusat <rweikusat@mobileactivedefense.com> | ||
43 | Fixes: ec0d215f9420 ("af_unix: fix 'poll for write'/connected DGRAM sockets") | ||
44 | Reviewed-by: Jason Baron <jbaron@akamai.com> | ||
45 | Signed-off-by: David S. Miller <davem@davemloft.net> | ||
46 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | ||
47 | Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> | ||
48 | --- | ||
49 | include/net/af_unix.h | 1 + | ||
50 | net/unix/af_unix.c | 183 ++++++++++++++++++++++++++++++++++++++++++++------ | ||
51 | 2 files changed, 165 insertions(+), 19 deletions(-) | ||
52 | |||
53 | diff --git a/include/net/af_unix.h b/include/net/af_unix.h | ||
54 | index dfe4ddf..e830c3d 100644 | ||
55 | --- a/include/net/af_unix.h | ||
56 | +++ b/include/net/af_unix.h | ||
57 | @@ -63,6 +63,7 @@ struct unix_sock { | ||
58 | #define UNIX_GC_CANDIDATE 0 | ||
59 | #define UNIX_GC_MAYBE_CYCLE 1 | ||
60 | struct socket_wq peer_wq; | ||
61 | + wait_queue_t peer_wake; | ||
62 | }; | ||
63 | |||
64 | static inline struct unix_sock *unix_sk(struct sock *sk) | ||
65 | diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c | ||
66 | index 76e6669..1975fd8 100644 | ||
67 | --- a/net/unix/af_unix.c | ||
68 | +++ b/net/unix/af_unix.c | ||
69 | @@ -316,6 +316,118 @@ found: | ||
70 | return s; | ||
71 | } | ||
72 | |||
73 | +/* Support code for asymmetrically connected dgram sockets | ||
74 | + * | ||
75 | + * If a datagram socket is connected to a socket not itself connected | ||
76 | + * to the first socket (eg, /dev/log), clients may only enqueue more | ||
77 | + * messages if the present receive queue of the server socket is not | ||
78 | + * "too large". This means there's a second writeability condition | ||
79 | + * poll and sendmsg need to test. The dgram recv code will do a wake | ||
80 | + * up on the peer_wait wait queue of a socket upon reception of a | ||
81 | + * datagram which needs to be propagated to sleeping would-be writers | ||
82 | + * since these might not have sent anything so far. This can't be | ||
83 | + * accomplished via poll_wait because the lifetime of the server | ||
84 | + * socket might be less than that of its clients if these break their | ||
85 | + * association with it or if the server socket is closed while clients | ||
86 | + * are still connected to it and there's no way to inform "a polling | ||
87 | + * implementation" that it should let go of a certain wait queue | ||
88 | + * | ||
89 | + * In order to propagate a wake up, a wait_queue_t of the client | ||
90 | + * socket is enqueued on the peer_wait queue of the server socket | ||
91 | + * whose wake function does a wake_up on the ordinary client socket | ||
92 | + * wait queue. This connection is established whenever a write (or | ||
93 | + * poll for write) hit the flow control condition and broken when the | ||
94 | + * association to the server socket is dissolved or after a wake up | ||
95 | + * was relayed. | ||
96 | + */ | ||
97 | + | ||
98 | +static int unix_dgram_peer_wake_relay(wait_queue_t *q, unsigned mode, int flags, | ||
99 | + void *key) | ||
100 | +{ | ||
101 | + struct unix_sock *u; | ||
102 | + wait_queue_head_t *u_sleep; | ||
103 | + | ||
104 | + u = container_of(q, struct unix_sock, peer_wake); | ||
105 | + | ||
106 | + __remove_wait_queue(&unix_sk(u->peer_wake.private)->peer_wait, | ||
107 | + q); | ||
108 | + u->peer_wake.private = NULL; | ||
109 | + | ||
110 | + /* relaying can only happen while the wq still exists */ | ||
111 | + u_sleep = sk_sleep(&u->sk); | ||
112 | + if (u_sleep) | ||
113 | + wake_up_interruptible_poll(u_sleep, key); | ||
114 | + | ||
115 | + return 0; | ||
116 | +} | ||
117 | + | ||
118 | +static int unix_dgram_peer_wake_connect(struct sock *sk, struct sock *other) | ||
119 | +{ | ||
120 | + struct unix_sock *u, *u_other; | ||
121 | + int rc; | ||
122 | + | ||
123 | + u = unix_sk(sk); | ||
124 | + u_other = unix_sk(other); | ||
125 | + rc = 0; | ||
126 | + spin_lock(&u_other->peer_wait.lock); | ||
127 | + | ||
128 | + if (!u->peer_wake.private) { | ||
129 | + u->peer_wake.private = other; | ||
130 | + __add_wait_queue(&u_other->peer_wait, &u->peer_wake); | ||
131 | + | ||
132 | + rc = 1; | ||
133 | + } | ||
134 | + | ||
135 | + spin_unlock(&u_other->peer_wait.lock); | ||
136 | + return rc; | ||
137 | +} | ||
138 | + | ||
139 | +static void unix_dgram_peer_wake_disconnect(struct sock *sk, | ||
140 | + struct sock *other) | ||
141 | +{ | ||
142 | + struct unix_sock *u, *u_other; | ||
143 | + | ||
144 | + u = unix_sk(sk); | ||
145 | + u_other = unix_sk(other); | ||
146 | + spin_lock(&u_other->peer_wait.lock); | ||
147 | + | ||
148 | + if (u->peer_wake.private == other) { | ||
149 | + __remove_wait_queue(&u_other->peer_wait, &u->peer_wake); | ||
150 | + u->peer_wake.private = NULL; | ||
151 | + } | ||
152 | + | ||
153 | + spin_unlock(&u_other->peer_wait.lock); | ||
154 | +} | ||
155 | + | ||
156 | +static void unix_dgram_peer_wake_disconnect_wakeup(struct sock *sk, | ||
157 | + struct sock *other) | ||
158 | +{ | ||
159 | + unix_dgram_peer_wake_disconnect(sk, other); | ||
160 | + wake_up_interruptible_poll(sk_sleep(sk), | ||
161 | + POLLOUT | | ||
162 | + POLLWRNORM | | ||
163 | + POLLWRBAND); | ||
164 | +} | ||
165 | + | ||
166 | +/* preconditions: | ||
167 | + * - unix_peer(sk) == other | ||
168 | + * - association is stable | ||
169 | + */ | ||
170 | +static int unix_dgram_peer_wake_me(struct sock *sk, struct sock *other) | ||
171 | +{ | ||
172 | + int connected; | ||
173 | + | ||
174 | + connected = unix_dgram_peer_wake_connect(sk, other); | ||
175 | + | ||
176 | + if (unix_recvq_full(other)) | ||
177 | + return 1; | ||
178 | + | ||
179 | + if (connected) | ||
180 | + unix_dgram_peer_wake_disconnect(sk, other); | ||
181 | + | ||
182 | + return 0; | ||
183 | +} | ||
184 | + | ||
185 | static inline int unix_writable(struct sock *sk) | ||
186 | { | ||
187 | return (atomic_read(&sk->sk_wmem_alloc) << 2) <= sk->sk_sndbuf; | ||
188 | @@ -420,6 +532,8 @@ static void unix_release_sock(struct sock *sk, int embrion) | ||
189 | skpair->sk_state_change(skpair); | ||
190 | sk_wake_async(skpair, SOCK_WAKE_WAITD, POLL_HUP); | ||
191 | } | ||
192 | + | ||
193 | + unix_dgram_peer_wake_disconnect(sk, skpair); | ||
194 | sock_put(skpair); /* It may now die */ | ||
195 | unix_peer(sk) = NULL; | ||
196 | } | ||
197 | @@ -648,6 +762,7 @@ static struct sock *unix_create1(struct net *net, struct socket *sock) | ||
198 | INIT_LIST_HEAD(&u->link); | ||
199 | mutex_init(&u->readlock); /* single task reading lock */ | ||
200 | init_waitqueue_head(&u->peer_wait); | ||
201 | + init_waitqueue_func_entry(&u->peer_wake, unix_dgram_peer_wake_relay); | ||
202 | unix_insert_socket(unix_sockets_unbound(sk), sk); | ||
203 | out: | ||
204 | if (sk == NULL) | ||
205 | @@ -1015,6 +1130,8 @@ restart: | ||
206 | if (unix_peer(sk)) { | ||
207 | struct sock *old_peer = unix_peer(sk); | ||
208 | unix_peer(sk) = other; | ||
209 | + unix_dgram_peer_wake_disconnect_wakeup(sk, old_peer); | ||
210 | + | ||
211 | unix_state_double_unlock(sk, other); | ||
212 | |||
213 | if (other != old_peer) | ||
214 | @@ -1453,6 +1570,7 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, | ||
215 | struct scm_cookie scm; | ||
216 | int max_level; | ||
217 | int data_len = 0; | ||
218 | + int sk_locked; | ||
219 | |||
220 | wait_for_unix_gc(); | ||
221 | err = scm_send(sock, msg, &scm, false); | ||
222 | @@ -1532,12 +1650,14 @@ restart: | ||
223 | goto out_free; | ||
224 | } | ||
225 | |||
226 | + sk_locked = 0; | ||
227 | unix_state_lock(other); | ||
228 | +restart_locked: | ||
229 | err = -EPERM; | ||
230 | if (!unix_may_send(sk, other)) | ||
231 | goto out_unlock; | ||
232 | |||
233 | - if (sock_flag(other, SOCK_DEAD)) { | ||
234 | + if (unlikely(sock_flag(other, SOCK_DEAD))) { | ||
235 | /* | ||
236 | * Check with 1003.1g - what should | ||
237 | * datagram error | ||
238 | @@ -1545,10 +1665,14 @@ restart: | ||
239 | unix_state_unlock(other); | ||
240 | sock_put(other); | ||
241 | |||
242 | + if (!sk_locked) | ||
243 | + unix_state_lock(sk); | ||
244 | + | ||
245 | err = 0; | ||
246 | - unix_state_lock(sk); | ||
247 | if (unix_peer(sk) == other) { | ||
248 | unix_peer(sk) = NULL; | ||
249 | + unix_dgram_peer_wake_disconnect_wakeup(sk, other); | ||
250 | + | ||
251 | unix_state_unlock(sk); | ||
252 | |||
253 | unix_dgram_disconnected(sk, other); | ||
254 | @@ -1574,21 +1698,38 @@ restart: | ||
255 | goto out_unlock; | ||
256 | } | ||
257 | |||
258 | - if (unix_peer(other) != sk && unix_recvq_full(other)) { | ||
259 | - if (!timeo) { | ||
260 | - err = -EAGAIN; | ||
261 | - goto out_unlock; | ||
262 | + if (unlikely(unix_peer(other) != sk && unix_recvq_full(other))) { | ||
263 | + if (timeo) { | ||
264 | + timeo = unix_wait_for_peer(other, timeo); | ||
265 | + | ||
266 | + err = sock_intr_errno(timeo); | ||
267 | + if (signal_pending(current)) | ||
268 | + goto out_free; | ||
269 | + | ||
270 | + goto restart; | ||
271 | } | ||
272 | |||
273 | - timeo = unix_wait_for_peer(other, timeo); | ||
274 | + if (!sk_locked) { | ||
275 | + unix_state_unlock(other); | ||
276 | + unix_state_double_lock(sk, other); | ||
277 | + } | ||
278 | |||
279 | - err = sock_intr_errno(timeo); | ||
280 | - if (signal_pending(current)) | ||
281 | - goto out_free; | ||
282 | + if (unix_peer(sk) != other || | ||
283 | + unix_dgram_peer_wake_me(sk, other)) { | ||
284 | + err = -EAGAIN; | ||
285 | + sk_locked = 1; | ||
286 | + goto out_unlock; | ||
287 | + } | ||
288 | |||
289 | - goto restart; | ||
290 | + if (!sk_locked) { | ||
291 | + sk_locked = 1; | ||
292 | + goto restart_locked; | ||
293 | + } | ||
294 | } | ||
295 | |||
296 | + if (unlikely(sk_locked)) | ||
297 | + unix_state_unlock(sk); | ||
298 | + | ||
299 | if (sock_flag(other, SOCK_RCVTSTAMP)) | ||
300 | __net_timestamp(skb); | ||
301 | maybe_add_creds(skb, sock, other); | ||
302 | @@ -1602,6 +1743,8 @@ restart: | ||
303 | return len; | ||
304 | |||
305 | out_unlock: | ||
306 | + if (sk_locked) | ||
307 | + unix_state_unlock(sk); | ||
308 | unix_state_unlock(other); | ||
309 | out_free: | ||
310 | kfree_skb(skb); | ||
311 | @@ -2245,14 +2388,16 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, | ||
312 | return mask; | ||
313 | |||
314 | writable = unix_writable(sk); | ||
315 | - other = unix_peer_get(sk); | ||
316 | - if (other) { | ||
317 | - if (unix_peer(other) != sk) { | ||
318 | - sock_poll_wait(file, &unix_sk(other)->peer_wait, wait); | ||
319 | - if (unix_recvq_full(other)) | ||
320 | - writable = 0; | ||
321 | - } | ||
322 | - sock_put(other); | ||
323 | + if (writable) { | ||
324 | + unix_state_lock(sk); | ||
325 | + | ||
326 | + other = unix_peer(sk); | ||
327 | + if (other && unix_peer(other) != sk && | ||
328 | + unix_recvq_full(other) && | ||
329 | + unix_dgram_peer_wake_me(sk, other)) | ||
330 | + writable = 0; | ||
331 | + | ||
332 | + unix_state_unlock(sk); | ||
333 | } | ||
334 | |||
335 | if (writable) | ||
336 | -- | ||
337 | cgit v0.12 | ||
338 | |||
diff --git a/recipes-kernel/linux/linux-hierofalcon_3.19.bb b/recipes-kernel/linux/linux-hierofalcon_3.19.bb index 5b3bd5e..92f6664 100644 --- a/recipes-kernel/linux/linux-hierofalcon_3.19.bb +++ b/recipes-kernel/linux/linux-hierofalcon_3.19.bb | |||
@@ -33,6 +33,7 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-3.19;branch="standard/qemuarm6 | |||
33 | file://dcache-CVE-2015-2925.patch \ | 33 | file://dcache-CVE-2015-2925.patch \ |
34 | file://virtio-net-CVE-2015-5156.patch \ | 34 | file://virtio-net-CVE-2015-5156.patch \ |
35 | file://ipc-CVE-2015-7613.patch \ | 35 | file://ipc-CVE-2015-7613.patch \ |
36 | file://net-unix-CVE-2013-7446.patch \ | ||
36 | " | 37 | " |
37 | 38 | ||
38 | S = "${WORKDIR}/git" | 39 | S = "${WORKDIR}/git" |
diff --git a/recipes-kernel/linux/linux-hierofalcon_4.1.bb b/recipes-kernel/linux/linux-hierofalcon_4.1.bb index 85ded8c..f0b6207 100644 --- a/recipes-kernel/linux/linux-hierofalcon_4.1.bb +++ b/recipes-kernel/linux/linux-hierofalcon_4.1.bb | |||
@@ -32,6 +32,7 @@ SRC_URI = "git://git.yoctoproject.org/linux-yocto-4.1;branch="standard/qemuarm64 | |||
32 | file://dcache-CVE-2015-2925.patch \ | 32 | file://dcache-CVE-2015-2925.patch \ |
33 | file://virtio-net-CVE-2015-5156.patch \ | 33 | file://virtio-net-CVE-2015-5156.patch \ |
34 | file://ipc-CVE-2015-7613.patch \ | 34 | file://ipc-CVE-2015-7613.patch \ |
35 | file://net-unix-CVE-2013-7446.patch \ | ||
35 | " | 36 | " |
36 | 37 | ||
37 | S = "${WORKDIR}/git" | 38 | S = "${WORKDIR}/git" |