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; }