Commit message: VSOCK: Fix missing msg_namelen update in vsock_stream_recvmsg()

The code misses to update the msg_namelen member to 0 and therefore
makes net/socket.c leak the local, uninitialized sockaddr_storage
variable to userland -- 128 bytes of kernel stack memory.

Cc: Andy King <acking@vmware.com>
Cc: Dmitry Torokhov <dtor@vmware.com>
Cc: George Zhang <georgezhang@vmware.com>
Signed-off-by: Mathias Krause <minipli@googlemail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
CVE: CVE-2013-3237
CWE: CWE-200
Vulnerability description: The vsock_stream_sendmsg function in net/vmw_vsock/af_vsock.c in the Linux kernel before 3.9-rc7 does not initialize a certain length variable, which allows local users to obtain sensitive information from kernel stack memory via a crafted recvmsg or recvfrom system call.

Project: linux
File: af_vsock.c

static struct sock *__vsock_find_bound_socket(struct sockaddr_vm *addr)
{
	struct vsock_sock *vsk;

	list_for_each_entry(vsk, vsock_bound_sockets(addr), bound_table)
		if (addr->svm_port == vsk->local_addr.svm_port)
			return sk_vsock(vsk);

	return NULL;
}

Project: linux
File: af_vsock.c

static void __vsock_release(struct sock *sk)
{
	if (sk) {
		struct sk_buff *skb;
		struct sock *pending;
		struct vsock_sock *vsk;

		vsk = vsock_sk(sk);
		pending = NULL;	/* Compiler warning. */

		if (vsock_in_bound_table(vsk))
			vsock_remove_bound(vsk);

		if (vsock_in_connected_table(vsk))
			vsock_remove_connected(vsk);

		transport->release(vsk);

		lock_sock(sk);
		sock_orphan(sk);
		sk->sk_shutdown = SHUTDOWN_MASK;

		while ((skb = skb_dequeue(&sk->sk_receive_queue)))
			kfree_skb(skb);

		/* Clean up any sockets that never were accepted. */
		while ((pending = vsock_dequeue_accept(sk)) != NULL) {
			__vsock_release(pending);
			sock_put(pending);
		}

		release_sock(sk);
		sock_put(sk);
	}
}

Project: linux
File: af_vsock.c

struct sock *vsock_find_bound_socket(struct sockaddr_vm *addr)
{
	struct sock *sk;

	spin_lock_bh(&vsock_table_lock);
	sk = __vsock_find_bound_socket(addr);
	if (sk)
		sock_hold(sk);

	spin_unlock_bh(&vsock_table_lock);

	return sk;
}

Project: linux
File: af_vsock.c

void vsock_for_each_connected_socket(void (*fn)(struct sock *sk))
{
	int i;

	spin_lock_bh(&vsock_table_lock);

	for (i = 0; i < ARRAY_SIZE(vsock_connected_table); i++) {
		struct vsock_sock *vsk;
		list_for_each_entry(vsk, &vsock_connected_table[i],
				    connected_table);
			fn(sk_vsock(vsk));
	}

	spin_unlock_bh(&vsock_table_lock);
}

Project: linux
File: af_vsock.c

static bool vsock_is_pending(struct sock *sk)
{
	struct vsock_sock *vsk = vsock_sk(sk);
	return !list_empty(&vsk->pending_links);
}

Project: linux
File: af_vsock.c

static int vsock_listen(struct socket *sock, int backlog)
{
	int err;
	struct sock *sk;
	struct vsock_sock *vsk;

	sk = sock->sk;

	lock_sock(sk);

	if (sock->type != SOCK_STREAM) {
		err = -EOPNOTSUPP;
		goto out;
	}

	if (sock->state != SS_UNCONNECTED) {
		err = -EINVAL;
		goto out;
	}

	vsk = vsock_sk(sk);

	if (!vsock_addr_bound(&vsk->local_addr)) {
		err = -EINVAL;
		goto out;
	}

	sk->sk_max_ack_backlog = backlog;
	sk->sk_state = SS_LISTEN;

	err = 0;

out:
	release_sock(sk);
	return err;
}

Project: linux
File: af_vsock.c

static int vsock_send_shutdown(struct sock *sk, int mode)
{
	return transport->shutdown(vsock_sk(sk), mode);
}

Project: linux
File: af_vsock.c

static int vsock_stream_getsockopt(struct socket *sock,
				   int level, int optname,
				   char __user *optval,
				   int __user *optlen)
{
	int err;
	int len;
	struct sock *sk;
	struct vsock_sock *vsk;
	u64 val;

	if (level != AF_VSOCK)
		return -ENOPROTOOPT;

	err = get_user(len, optlen);
	if (err != 0)
		return err;

#define COPY_OUT(_v)                            \
	do {					\
		if (len < sizeof(_v))		\
			return -EINVAL;		\
						\
		len = sizeof(_v);		\
		if (copy_to_user(optval, &_v, len) != 0)	\
			return -EFAULT;				\
								\
	} while (0)

	err = 0;
	sk = sock->sk;
	vsk = vsock_sk(sk);

	switch (optname) {
	case SO_VM_SOCKETS_BUFFER_SIZE:
		val = transport->get_buffer_size(vsk);
		COPY_OUT(val);
		break;

	case SO_VM_SOCKETS_BUFFER_MAX_SIZE:
		val = transport->get_max_buffer_size(vsk);
		COPY_OUT(val);
		break;

	case SO_VM_SOCKETS_BUFFER_MIN_SIZE:
		val = transport->get_min_buffer_size(vsk);
		COPY_OUT(val);
		break;

	case SO_VM_SOCKETS_CONNECT_TIMEOUT: {
		struct timeval tv;
		tv.tv_sec = vsk->connect_timeout / HZ;
		tv.tv_usec =
		    (vsk->connect_timeout -
		     tv.tv_sec * HZ) * (1000000 / HZ);
		COPY_OUT(tv);
		break;
	}
	default:
		return -ENOPROTOOPT;
	}

	err = put_user(len, optlen);
	if (err != 0)
		return -EFAULT;

#undef COPY_OUT

	return 0;
}

Project: linux
File: af_vsock.c

static int vsock_stream_setsockopt(struct socket *sock,
				   int level,
				   int optname,
				   char __user *optval,
				   unsigned int optlen)
{
	int err;
	struct sock *sk;
	struct vsock_sock *vsk;
	u64 val;

	if (level != AF_VSOCK)
		return -ENOPROTOOPT;

#define COPY_IN(_v)                                       \
	do {						  \
		if (optlen < sizeof(_v)) {		  \
			err = -EINVAL;			  \
			goto exit;			  \
		}					  \
		if (copy_from_user(&_v, optval, sizeof(_v)) != 0) {	\
			err = -EFAULT;					\
			goto exit;					\
		}							\
	} while (0)

	err = 0;
	sk = sock->sk;
	vsk = vsock_sk(sk);

	lock_sock(sk);

	switch (optname) {
	case SO_VM_SOCKETS_BUFFER_SIZE:
		COPY_IN(val);
		transport->set_buffer_size(vsk, val);
		break;

	case SO_VM_SOCKETS_BUFFER_MAX_SIZE:
		COPY_IN(val);
		transport->set_max_buffer_size(vsk, val);
		break;

	case SO_VM_SOCKETS_BUFFER_MIN_SIZE:
		COPY_IN(val);
		transport->set_min_buffer_size(vsk, val);
		break;

	case SO_VM_SOCKETS_CONNECT_TIMEOUT: {
		struct timeval tv;
		COPY_IN(tv);
		if (tv.tv_sec >= 0 && tv.tv_usec < USEC_PER_SEC &&
		    tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1)) {
			vsk->connect_timeout = tv.tv_sec * HZ +
			    DIV_ROUND_UP(tv.tv_usec, (1000000 / HZ));
			if (vsk->connect_timeout == 0)
				vsk->connect_timeout =
				    VSOCK_DEFAULT_CONNECT_TIMEOUT;

		} else {
			err = -ERANGE;
		}
		break;
	}

	default:
		err = -ENOPROTOOPT;
		break;
	}

#undef COPY_IN

exit:
	release_sock(sk);
	return err;
}