summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSona Sarmadi <sona.sarmadi@enea.com>2016-03-16 07:18:33 (GMT)
committerTudor Florea <tudor.florea@enea.com>2016-03-17 02:06:30 (GMT)
commit969125816231fc089be31e02c804f594713e8f56 (patch)
treed04ba2a33853feddab63aa59fd06eb726648c986
parent227e772457791f69b69646556b3ffbbb94936bd0 (diff)
downloadmeta-hierofalcon-969125816231fc089be31e02c804f594713e8f56.tar.gz
net-unix: CVE-2013-7446
Unix sockets use after free - peer_wait_queue prematurely freed Reference: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-7446 References to upstream patch: https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/patch/?id=5c77e26862ce604edea05b3442ed765e9756fe0f Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> Signed-off-by: Tudor Florea <tudor.florea@enea.com>
-rw-r--r--recipes-kernel/linux/linux-hierofalcon/net-unix-CVE-2013-7446.patch338
-rw-r--r--recipes-kernel/linux/linux-hierofalcon_3.19.bb1
-rw-r--r--recipes-kernel/linux/linux-hierofalcon_4.1.bb1
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 @@
1From 5c77e26862ce604edea05b3442ed765e9756fe0f Mon Sep 17 00:00:00 2001
2From: Rainer Weikusat <rweikusat@mobileactivedefense.com>
3Date: Fri, 20 Nov 2015 22:07:23 +0000
4Subject: unix: avoid use-after-free in ep_remove_wait_queue
5
6[ Upstream commit 7d267278a9ece963d77eefec61630223fce08c6c ]
7
8Rainer Weikusat <rweikusat@mobileactivedefense.com> writes:
9An AF_UNIX datagram socket being the client in an n:1 association with
10some server socket is only allowed to send messages to the server if the
11receive queue of this socket contains at most sk_max_ack_backlog
12datagrams. This implies that prospective writers might be forced to go
13to sleep despite none of the message presently enqueued on the server
14receive queue were sent by them. In order to ensure that these will be
15woken up once space becomes again available, the present unix_dgram_poll
16routine does a second sock_poll_wait call with the peer_wait wait queue
17of the server socket as queue argument (unix_dgram_recvmsg does a wake
18up on this queue after a datagram was received). This is inherently
19problematic because the server socket is only guaranteed to remain alive
20for as long as the client still holds a reference to it. In case the
21connection is dissolved via connect or by the dead peer detection logic
22in unix_dgram_sendmsg, the server socket may be freed despite "the
23polling mechanism" (in particular, epoll) still has a pointer to the
24corresponding peer_wait queue. There's no way to forcibly deregister a
25wait queue with epoll.
26
27Based on an idea by Jason Baron, the patch below changes the code such
28that a wait_queue_t belonging to the client socket is enqueued on the
29peer_wait queue of the server whenever the peer receive queue full
30condition is detected by either a sendmsg or a poll. A wake up on the
31peer queue is then relayed to the ordinary wait queue of the client
32socket via wake function. The connection to the peer wait queue is again
33dissolved if either a wake up is about to be relayed or the client
34socket reconnects or a dead peer is detected or the client socket is
35itself closed. This enables removing the second sock_poll_wait from
36unix_dgram_poll, thus avoiding the use-after-free, while still ensuring
37that no blocked writer sleeps forever.
38
39CVE: CVE-2013-7446.
40Upstream-Status: Backport
41
42Signed-off-by: Rainer Weikusat <rweikusat@mobileactivedefense.com>
43Fixes: ec0d215f9420 ("af_unix: fix 'poll for write'/connected DGRAM sockets")
44Reviewed-by: Jason Baron <jbaron@akamai.com>
45Signed-off-by: David S. Miller <davem@davemloft.net>
46Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
47Signed-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
53diff --git a/include/net/af_unix.h b/include/net/af_unix.h
54index 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)
65diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
66index 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--
337cgit 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
38S = "${WORKDIR}/git" 39S = "${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
37S = "${WORKDIR}/git" 38S = "${WORKDIR}/git"