6d027eb
Bugzilla: 1035887
6d027eb
Upstream-status: 3.13
6d027eb
6d027eb
From bceaa90240b6019ed73b49965eac7d167610be69 Mon Sep 17 00:00:00 2001
6d027eb
From: Hannes Frederic Sowa <hannes@stressinduktion.org>
6d027eb
Date: Mon, 18 Nov 2013 04:20:45 +0100
6d027eb
Subject: [PATCH] inet: prevent leakage of uninitialized memory to user in recv
6d027eb
 syscalls
6d027eb
6d027eb
Only update *addr_len when we actually fill in sockaddr, otherwise we
6d027eb
can return uninitialized memory from the stack to the caller in the
6d027eb
recvfrom, recvmmsg and recvmsg syscalls. Drop the the (addr_len == NULL)
6d027eb
checks because we only get called with a valid addr_len pointer either
6d027eb
from sock_common_recvmsg or inet_recvmsg.
6d027eb
6d027eb
If a blocking read waits on a socket which is concurrently shut down we
6d027eb
now return zero and set msg_msgnamelen to 0.
6d027eb
6d027eb
Reported-by: mpb <mpb.mail@gmail.com>
6d027eb
Suggested-by: Eric Dumazet <eric.dumazet@gmail.com>
6d027eb
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
6d027eb
Signed-off-by: David S. Miller <davem@davemloft.net>
6d027eb
---
6d027eb
 net/ieee802154/dgram.c |  3 +--
6d027eb
 net/ipv4/ping.c        | 19 +++++++------------
6d027eb
 net/ipv4/raw.c         |  4 +---
6d027eb
 net/ipv4/udp.c         |  7 +------
6d027eb
 net/ipv6/raw.c         |  4 +---
6d027eb
 net/ipv6/udp.c         |  5 +----
6d027eb
 net/l2tp/l2tp_ip.c     |  4 +---
6d027eb
 net/phonet/datagram.c  |  9 ++++-----
6d027eb
 8 files changed, 17 insertions(+), 38 deletions(-)
6d027eb
6d027eb
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
6d027eb
index 581a595..1865fdf 100644
6d027eb
--- a/net/ieee802154/dgram.c
6d027eb
+++ b/net/ieee802154/dgram.c
6d027eb
@@ -315,9 +315,8 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 	if (saddr) {
6d027eb
 		saddr->family = AF_IEEE802154;
6d027eb
 		saddr->addr = mac_cb(skb)->sa;
6d027eb
-	}
6d027eb
-	if (addr_len)
6d027eb
 		*addr_len = sizeof(*saddr);
6d027eb
+	}
6d027eb
 
6d027eb
 	if (flags & MSG_TRUNC)
6d027eb
 		copied = skb->len;
6d027eb
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
6d027eb
index 9afbdb1..aacefa0 100644
6d027eb
--- a/net/ipv4/ping.c
6d027eb
+++ b/net/ipv4/ping.c
6d027eb
@@ -830,8 +830,6 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 {
6d027eb
 	struct inet_sock *isk = inet_sk(sk);
6d027eb
 	int family = sk->sk_family;
6d027eb
-	struct sockaddr_in *sin;
6d027eb
-	struct sockaddr_in6 *sin6;
6d027eb
 	struct sk_buff *skb;
6d027eb
 	int copied, err;
6d027eb
 
6d027eb
@@ -841,13 +839,6 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 	if (flags & MSG_OOB)
6d027eb
 		goto out;
6d027eb
 
6d027eb
-	if (addr_len) {
6d027eb
-		if (family == AF_INET)
6d027eb
-			*addr_len = sizeof(*sin);
6d027eb
-		else if (family == AF_INET6 && addr_len)
6d027eb
-			*addr_len = sizeof(*sin6);
6d027eb
-	}
6d027eb
-
6d027eb
 	if (flags & MSG_ERRQUEUE) {
6d027eb
 		if (family == AF_INET) {
6d027eb
 			return ip_recv_error(sk, msg, len);
6d027eb
@@ -877,11 +868,13 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 
6d027eb
 	/* Copy the address and add cmsg data. */
6d027eb
 	if (family == AF_INET) {
6d027eb
-		sin = (struct sockaddr_in *) msg->msg_name;
6d027eb
+		struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
6d027eb
+
6d027eb
 		sin->sin_family = AF_INET;
6d027eb
 		sin->sin_port = 0 /* skb->h.uh->source */;
6d027eb
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
6d027eb
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
6d027eb
+		*addr_len = sizeof(*sin);
6d027eb
 
6d027eb
 		if (isk->cmsg_flags)
6d027eb
 			ip_cmsg_recv(msg, skb);
6d027eb
@@ -890,17 +883,19 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 	} else if (family == AF_INET6) {
6d027eb
 		struct ipv6_pinfo *np = inet6_sk(sk);
6d027eb
 		struct ipv6hdr *ip6 = ipv6_hdr(skb);
6d027eb
-		sin6 = (struct sockaddr_in6 *) msg->msg_name;
6d027eb
+		struct sockaddr_in6 *sin6 =
6d027eb
+			(struct sockaddr_in6 *)msg->msg_name;
6d027eb
+
6d027eb
 		sin6->sin6_family = AF_INET6;
6d027eb
 		sin6->sin6_port = 0;
6d027eb
 		sin6->sin6_addr = ip6->saddr;
6d027eb
-
6d027eb
 		sin6->sin6_flowinfo = 0;
6d027eb
 		if (np->sndflow)
6d027eb
 			sin6->sin6_flowinfo = ip6_flowinfo(ip6);
6d027eb
 
6d027eb
 		sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
6d027eb
 							  IP6CB(skb)->iif);
6d027eb
+		*addr_len = sizeof(*sin6);
6d027eb
 
6d027eb
 		if (inet6_sk(sk)->rxopt.all)
6d027eb
 			pingv6_ops.ip6_datagram_recv_ctl(sk, msg, skb);
6d027eb
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
6d027eb
index 41e1d28..5cb8ddb 100644
6d027eb
--- a/net/ipv4/raw.c
6d027eb
+++ b/net/ipv4/raw.c
6d027eb
@@ -696,9 +696,6 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 	if (flags & MSG_OOB)
6d027eb
 		goto out;
6d027eb
 
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len = sizeof(*sin);
6d027eb
-
6d027eb
 	if (flags & MSG_ERRQUEUE) {
6d027eb
 		err = ip_recv_error(sk, msg, len);
6d027eb
 		goto out;
6d027eb
@@ -726,6 +723,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
6d027eb
 		sin->sin_port = 0;
6d027eb
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
6d027eb
+		*addr_len = sizeof(*sin);
6d027eb
 	}
6d027eb
 	if (inet->cmsg_flags)
6d027eb
 		ip_cmsg_recv(msg, skb);
6d027eb
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
6d027eb
index 89909dd..998431c 100644
6d027eb
--- a/net/ipv4/udp.c
6d027eb
+++ b/net/ipv4/udp.c
6d027eb
@@ -1235,12 +1235,6 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
6d027eb
 	int is_udplite = IS_UDPLITE(sk);
6d027eb
 	bool slow;
6d027eb
 
6d027eb
-	/*
6d027eb
-	 *	Check any passed addresses
6d027eb
-	 */
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len = sizeof(*sin);
6d027eb
-
6d027eb
 	if (flags & MSG_ERRQUEUE)
6d027eb
 		return ip_recv_error(sk, msg, len);
6d027eb
 
6d027eb
@@ -1302,6 +1296,7 @@ try_again:
6d027eb
 		sin->sin_port = udp_hdr(skb)->source;
6d027eb
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
6d027eb
 		memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
6d027eb
+		*addr_len = sizeof(*sin);
6d027eb
 	}
6d027eb
 	if (inet->cmsg_flags)
6d027eb
 		ip_cmsg_recv(msg, skb);
6d027eb
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
6d027eb
index 3c00842..e24ff1d 100644
6d027eb
--- a/net/ipv6/raw.c
6d027eb
+++ b/net/ipv6/raw.c
6d027eb
@@ -465,9 +465,6 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 	if (flags & MSG_OOB)
6d027eb
 		return -EOPNOTSUPP;
6d027eb
 
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len=sizeof(*sin6);
6d027eb
-
6d027eb
 	if (flags & MSG_ERRQUEUE)
6d027eb
 		return ipv6_recv_error(sk, msg, len);
6d027eb
 
6d027eb
@@ -506,6 +503,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 		sin6->sin6_flowinfo = 0;
6d027eb
 		sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,
6d027eb
 							  IP6CB(skb)->iif);
6d027eb
+		*addr_len = sizeof(*sin6);
6d027eb
 	}
6d027eb
 
6d027eb
 	sock_recv_ts_and_drops(msg, sk, skb);
6d027eb
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
6d027eb
index f3893e8..81eb8cf 100644
6d027eb
--- a/net/ipv6/udp.c
6d027eb
+++ b/net/ipv6/udp.c
6d027eb
@@ -392,9 +392,6 @@ int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 	int is_udp4;
6d027eb
 	bool slow;
6d027eb
 
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len = sizeof(struct sockaddr_in6);
6d027eb
-
6d027eb
 	if (flags & MSG_ERRQUEUE)
6d027eb
 		return ipv6_recv_error(sk, msg, len);
6d027eb
 
6d027eb
@@ -480,7 +477,7 @@ try_again:
6d027eb
 				ipv6_iface_scope_id(&sin6->sin6_addr,
6d027eb
 						    IP6CB(skb)->iif);
6d027eb
 		}
6d027eb
-
6d027eb
+		*addr_len = sizeof(*sin6);
6d027eb
 	}
6d027eb
 	if (is_udp4) {
6d027eb
 		if (inet->cmsg_flags)
6d027eb
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
6d027eb
index 571db8d..da1a1ce 100644
6d027eb
--- a/net/l2tp/l2tp_ip.c
6d027eb
+++ b/net/l2tp/l2tp_ip.c
6d027eb
@@ -518,9 +518,6 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
6d027eb
 	if (flags & MSG_OOB)
6d027eb
 		goto out;
6d027eb
 
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len = sizeof(*sin);
6d027eb
-
6d027eb
 	skb = skb_recv_datagram(sk, flags, noblock, &err;;
6d027eb
 	if (!skb)
6d027eb
 		goto out;
6d027eb
@@ -543,6 +540,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
6d027eb
 		sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
6d027eb
 		sin->sin_port = 0;
6d027eb
 		memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
6d027eb
+		*addr_len = sizeof(*sin);
6d027eb
 	}
6d027eb
 	if (inet->cmsg_flags)
6d027eb
 		ip_cmsg_recv(msg, skb);
6d027eb
diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c
6d027eb
index 12c30f3..38946b2 100644
6d027eb
--- a/net/phonet/datagram.c
6d027eb
+++ b/net/phonet/datagram.c
6d027eb
@@ -139,9 +139,6 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 			MSG_CMSG_COMPAT))
6d027eb
 		goto out_nofree;
6d027eb
 
6d027eb
-	if (addr_len)
6d027eb
-		*addr_len = sizeof(sa);
6d027eb
-
6d027eb
 	skb = skb_recv_datagram(sk, flags, noblock, &rval);
6d027eb
 	if (skb == NULL)
6d027eb
 		goto out_nofree;
6d027eb
@@ -162,8 +159,10 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
6d027eb
 
6d027eb
 	rval = (flags & MSG_TRUNC) ? skb->len : copylen;
6d027eb
 
6d027eb
-	if (msg->msg_name != NULL)
6d027eb
-		memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn));
6d027eb
+	if (msg->msg_name != NULL) {
6d027eb
+		memcpy(msg->msg_name, &sa, sizeof(sa));
6d027eb
+		*addr_len = sizeof(sa);
6d027eb
+	}
6d027eb
 
6d027eb
 out:
6d027eb
 	skb_free_datagram(sk, skb);
6d027eb
-- 
6d027eb
1.8.3.1
6d027eb