Qemu doesn't check socklen_t values before using them.  If a value is
provided that is large (say -1) then qemu will merrily use it in
alloca() (which will blow the stack).  The kernel checks all socklen_t
values for < 0 or > MAX_SOCK_ADDR.

This patch mimics the kernel behavior - which prevents SEGVs.
Index: qemu/linux-user/syscall.c
===================================================================
--- qemu.orig/linux-user/syscall.c	2007-12-17 21:16:47.000000000 -0700
+++ qemu/linux-user/syscall.c	2007-12-17 21:51:14.000000000 -0700
@@ -1221,11 +1221,19 @@
     return get_errno(socket(domain, type, protocol));
 }
 
+/* MAX_SOCK_ADDR from linux/net/socket.c */
+#define MAX_SOCK_ADDR	128
+
 /* do_bind() Must return target values and target errnos. */
 static abi_long do_bind(int sockfd, abi_ulong target_saddr_addr,
                         socklen_t addrlen)
 {
-    void *addr = alloca(addrlen);
+    void *addr;
+
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
+    addr = alloca(addrlen);
 
     if (copy_from_user_sockaddr(addr, target_saddr_addr, addrlen))
         return -TARGET_EFAULT;
@@ -1237,7 +1245,12 @@
 static abi_long do_connect(int sockfd, abi_ulong target_saddr_addr,
                            socklen_t addrlen)
 {
-    void *addr = alloca(addrlen);
+    void *addr;
+
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
+    addr = alloca(addrlen);
 
     if (copy_from_user_sockaddr(addr, target_saddr_addr, addrlen))
         return -TARGET_EFAULT;
@@ -1318,6 +1331,9 @@
     if (get_user_u32(addrlen, target_addrlen_addr))
         return -TARGET_EINVAL;
 
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
     addr = alloca(addrlen);
 
     ret = get_errno(accept(fd, addr, &addrlen));
@@ -1340,6 +1356,9 @@
     if (get_user_u32(addrlen, target_addrlen_addr))
         return -TARGET_EFAULT;
 
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
     addr = alloca(addrlen);
 
     ret = get_errno(getpeername(fd, addr, &addrlen));
@@ -1362,6 +1381,9 @@
     if (get_user_u32(addrlen, target_addrlen_addr))
         return -TARGET_EFAULT;
 
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
     addr = alloca(addrlen);
 
     ret = get_errno(getsockname(fd, addr, &addrlen));
@@ -1397,9 +1419,13 @@
     void *host_msg;
     abi_long ret;
 
+    if (addrlen < 0 || addrlen > MAX_SOCK_ADDR)
+        return -TARGET_EINVAL;
+
     host_msg = lock_user(VERIFY_READ, msg, len, 1);
     if (!host_msg)
         return -TARGET_EFAULT;
+
     if (target_saddr_addr) {
         addr = alloca(addrlen);
         if (copy_from_user_sockaddr(addr, target_saddr_addr, addrlen))
@@ -1415,7 +1441,7 @@
 /* do_recvfrom() Must return target values and target errnos. */
 static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags,
                             abi_ulong target_saddr_addr,
-                            abi_ulong target_addrlen)
+                            abi_ulong target_addrlen_addr)
 {
     socklen_t addrlen;
     void *addr;
@@ -1426,10 +1452,14 @@
         return -TARGET_EFAULT;
 
     if (target_saddr_addr) {
-        if (get_user_u32(addrlen, target_addrlen)) {
+        if (get_user_u32(addrlen, target_addrlen_addr)) {
             ret = -TARGET_EFAULT;
             goto fail;
         }
+        if (addrlen < 0 || addrlen > MAX_SOCK_ADDR) {
+            ret = -TARGET_EINVAL;
+            goto fail;
+        }
         if (addrlen > sizeof(struct sockaddr_storage)) {
             ret = -TARGET_EINVAL;
             goto fail;
@@ -1444,7 +1474,7 @@
     if (!is_error(ret)) {
         if (target_saddr_addr) {
             if (copy_to_user_sockaddr(target_saddr_addr, addr, addrlen)
-                || put_user_u32(addrlen, target_addrlen)) {
+                || put_user_u32(addrlen, target_addrlen_addr)) {
                 ret = -TARGET_EFAULT;
                 goto fail;
             }

Reply via email to