linux/net
Weiping Pan b2c1fcae04 rds: set correct msg_namelen
commit 06b6a1cf6e upstream.

Jay Fenlason (fenlason@redhat.com) found a bug,
that recvfrom() on an RDS socket can return the contents of random kernel
memory to userspace if it was called with a address length larger than
sizeof(struct sockaddr_in).
rds_recvmsg() also fails to set the addr_len paramater properly before
returning, but that's just a bug.
There are also a number of cases wher recvfrom() can return an entirely bogus
address. Anything in rds_recvmsg() that returns a non-negative value but does
not go through the "sin = (struct sockaddr_in *)msg->msg_name;" code path
at the end of the while(1) loop will return up to 128 bytes of kernel memory
to userspace.

And I write two test programs to reproduce this bug, you will see that in
rds_server, fromAddr will be overwritten and the following sock_fd will be
destroyed.
Yes, it is the programmer's fault to set msg_namelen incorrectly, but it is
better to make the kernel copy the real length of address to user space in
such case.

How to run the test programs ?
I test them on 32bit x86 system, 3.5.0-rc7.

1 compile
gcc -o rds_client rds_client.c
gcc -o rds_server rds_server.c

2 run ./rds_server on one console

3 run ./rds_client on another console

4 you will see something like:
server is waiting to receive data...
old socket fd=3
server received data from client:data from client
msg.msg_namelen=32
new socket fd=-1067277685
sendmsg()
: Bad file descriptor

/***************** rds_client.c ********************/

int main(void)
{
	int sock_fd;
	struct sockaddr_in serverAddr;
	struct sockaddr_in toAddr;
	char recvBuffer[128] = "data from client";
	struct msghdr msg;
	struct iovec iov;

	sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
	if (sock_fd < 0) {
		perror("create socket error\n");
		exit(1);
	}

	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serverAddr.sin_port = htons(4001);

	if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
		perror("bind() error\n");
		close(sock_fd);
		exit(1);
	}

	memset(&toAddr, 0, sizeof(toAddr));
	toAddr.sin_family = AF_INET;
	toAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	toAddr.sin_port = htons(4000);
	msg.msg_name = &toAddr;
	msg.msg_namelen = sizeof(toAddr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_iov->iov_base = recvBuffer;
	msg.msg_iov->iov_len = strlen(recvBuffer) + 1;
	msg.msg_control = 0;
	msg.msg_controllen = 0;
	msg.msg_flags = 0;

	if (sendmsg(sock_fd, &msg, 0) == -1) {
		perror("sendto() error\n");
		close(sock_fd);
		exit(1);
	}

	printf("client send data:%s\n", recvBuffer);

	memset(recvBuffer, '\0', 128);

	msg.msg_name = &toAddr;
	msg.msg_namelen = sizeof(toAddr);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_iov->iov_base = recvBuffer;
	msg.msg_iov->iov_len = 128;
	msg.msg_control = 0;
	msg.msg_controllen = 0;
	msg.msg_flags = 0;
	if (recvmsg(sock_fd, &msg, 0) == -1) {
		perror("recvmsg() error\n");
		close(sock_fd);
		exit(1);
	}

	printf("receive data from server:%s\n", recvBuffer);

	close(sock_fd);

	return 0;
}

/***************** rds_server.c ********************/

int main(void)
{
	struct sockaddr_in fromAddr;
	int sock_fd;
	struct sockaddr_in serverAddr;
	unsigned int addrLen;
	char recvBuffer[128];
	struct msghdr msg;
	struct iovec iov;

	sock_fd = socket(AF_RDS, SOCK_SEQPACKET, 0);
	if(sock_fd < 0) {
		perror("create socket error\n");
		exit(0);
	}

	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	serverAddr.sin_port = htons(4000);
	if (bind(sock_fd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
		perror("bind error\n");
		close(sock_fd);
		exit(1);
	}

	printf("server is waiting to receive data...\n");
	msg.msg_name = &fromAddr;

	/*
	 * I add 16 to sizeof(fromAddr), ie 32,
	 * and pay attention to the definition of fromAddr,
	 * recvmsg() will overwrite sock_fd,
	 * since kernel will copy 32 bytes to userspace.
	 *
	 * If you just use sizeof(fromAddr), it works fine.
	 * */
	msg.msg_namelen = sizeof(fromAddr) + 16;
	/* msg.msg_namelen = sizeof(fromAddr); */
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_iov->iov_base = recvBuffer;
	msg.msg_iov->iov_len = 128;
	msg.msg_control = 0;
	msg.msg_controllen = 0;
	msg.msg_flags = 0;

	while (1) {
		printf("old socket fd=%d\n", sock_fd);
		if (recvmsg(sock_fd, &msg, 0) == -1) {
			perror("recvmsg() error\n");
			close(sock_fd);
			exit(1);
		}
		printf("server received data from client:%s\n", recvBuffer);
		printf("msg.msg_namelen=%d\n", msg.msg_namelen);
		printf("new socket fd=%d\n", sock_fd);
		strcat(recvBuffer, "--data from server");
		if (sendmsg(sock_fd, &msg, 0) == -1) {
			perror("sendmsg()\n");
			close(sock_fd);
			exit(1);
		}
	}

	close(sock_fd);
	return 0;
}

Signed-off-by: Weiping Pan <wpan@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2012-10-02 10:30:35 -07:00
..
9p 9p: BUG before corrupting memory 2012-06-22 11:37:15 -07:00
802 Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2012-04-02 17:53:39 -07:00
8021q net: Fix memory leak - vlan_info struct 2012-08-09 08:31:41 -07:00
appletalk
atm atm: fix info leak via getsockname() 2012-10-02 10:29:36 -07:00
ax25 net ax25: Reorder ax25_exit to remove races. 2012-04-19 15:37:48 -04:00
batman-adv batman-adv: only drop packets of known wifi clients 2012-07-16 09:04:11 -07:00
bluetooth Bluetooth: Fix sending a HCI Authorization Request over LE links 2012-10-02 10:30:34 -07:00
bridge bridge: Assign rtnl_link_ops to bridge devices created via ioctl (v2) 2012-07-16 09:03:49 -07:00
caif caif: Fix access to freed pernet memory 2012-08-09 08:31:42 -07:00
can net: remove skb_orphan_try() 2012-07-16 09:03:48 -07:00
ceph libceph: isolate kmap() call in write_partial_msg_pages() 2012-03-22 10:47:52 -05:00
core net: Statically initialize init_net.dev_base_head 2012-10-02 10:30:35 -07:00
dcb
dccp dccp: fix info leak via getsockopt(DCCP_SOCKOPT_CCID_TX_INFO) 2012-10-02 10:29:37 -07:00
decnet Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
dns_resolver KEYS: Allow special keyrings to be cleared 2012-01-19 14:38:51 +11:00
dsa dsa: Move switch drivers to new directory drivers/net/dsa 2011-11-29 00:21:36 -05:00
econet Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
ethernet Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
ieee802154 6lowpan: add missing spin_lock_init() 2012-04-26 05:32:55 -04:00
ipv4 net: ipv4: ipmr_expire_timer causes crash when removing net namespace 2012-10-02 10:29:50 -07:00
ipv6 ipv6: addrconf: Avoid calling netdevice notifiers with RCU read-side lock 2012-10-02 10:29:35 -07:00
ipx
irda Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
iucv net: remove skb_orphan_try() 2012-07-16 09:03:48 -07:00
key net/key/af_key.c: add missing kfree_skb 2012-04-13 11:01:44 -04:00
l2tp l2tp: avoid to use synchronize_rcu in tunnel free function 2012-10-02 10:29:42 -07:00
lapb Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
llc llc: fix info leak via getsockname() 2012-10-02 10:29:37 -07:00
mac80211 mac80211: clear bssid on auth/assoc failure 2012-10-02 10:30:07 -07:00
netfilter ipvs: fix info leak in getsockopt(IP_VS_SO_GET_TIMEOUT) 2012-10-02 10:29:37 -07:00
netlabel netlabel: use GFP flags from caller instead of GFP_ATOMIC 2012-03-22 19:29:57 -04:00
netlink netlink: fix possible spoofing from non-root processes 2012-10-02 10:29:38 -07:00
netrom Remove all #inclusions of asm/system.h 2012-03-28 18:30:03 +01:00
nfc NFC: Prevent multiple buffer overflows in NCI 2012-07-16 09:03:50 -07:00
openvswitch openvswitch: Reset upper layer protocol info on internal devices. 2012-10-02 10:29:50 -07:00
packet af_packet: don't emit packet on orig fanout group 2012-10-02 10:29:37 -07:00
phonet phonet: Sort out initiailziation and cleanup code. 2012-04-13 11:01:43 -04:00
rds rds: set correct msg_namelen 2012-10-02 10:30:35 -07:00
rfkill device.h: cleanup users outside of linux/include (C files) 2012-03-11 14:27:37 -04:00
rose Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2012-04-02 17:53:39 -07:00
rxrpc RxRPC: Fix kcalloc parameters swapped 2012-02-14 14:41:55 -05:00
sched net_sched: gact: Fix potential panic in tcf_gact(). 2012-10-02 10:29:34 -07:00
sctp sctp: Fix list corruption resulting from freeing an association on a list 2012-08-09 08:31:42 -07:00
sunrpc svcrpc: sends on closed socket should stop immediately 2012-09-14 10:00:19 -07:00
tipc tipc: Optimize setting of immutable payload message header fields 2012-02-29 11:45:35 -05:00
unix af_netlink: force credentials passing [CVE-2012-3520] 2012-10-02 10:29:37 -07:00
wanrouter wanmain: comparing array with NULL 2012-08-09 08:31:51 -07:00
wimax
wireless cfg80211: fix possible circular lock on reg_regdb_search() 2012-10-02 10:30:09 -07:00
x25 net:x25: use IS_ENABLED 2011-12-16 15:49:52 -05:00
xfrm ipv6: fix incorrect ipsec fragment 2012-06-10 00:36:15 +09:00
compat.c net: Fix references to out-of-scope variables in put_cmsg_compat() 2012-08-09 08:31:42 -07:00
Kconfig net: Add Open vSwitch kernel components. 2011-12-03 09:35:17 -08:00
Makefile net: Add Open vSwitch kernel components. 2011-12-03 09:35:17 -08:00
nonet.c
socket.c net: fix info leak in compat dev_ifconf() 2012-10-02 10:29:37 -07:00
sysctl_net.c sysctl: Modify __register_sysctl_paths to take a set instead of a root and an nsproxy 2012-01-24 16:40:30 -08:00