diff options
| author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-11-17 22:20:45 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-08 10:29:25 -0500 |
| commit | a2214488937a84d8d0b5a3b546f97b2485029a17 (patch) | |
| tree | 533a832124d21321da8413805ee548886bcbf0b1 /net/ipv4 | |
| parent | ac6a5b926d2d8df1596f7cb91f46afc18e5e39f9 (diff) | |
inet: prevent leakage of uninitialized memory to user in recv syscalls
[ Upstream commit bceaa90240b6019ed73b49965eac7d167610be69 ]
Only update *addr_len when we actually fill in sockaddr, otherwise we
can return uninitialized memory from the stack to the caller in the
recvfrom, recvmmsg and recvmsg syscalls. Drop the the (addr_len == NULL)
checks because we only get called with a valid addr_len pointer either
from sock_common_recvmsg or inet_recvmsg.
If a blocking read waits on a socket which is concurrently shut down we
now return zero and set msg_msgnamelen to 0.
Reported-by: mpb <mpb.mail@gmail.com>
Suggested-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net/ipv4')
| -rw-r--r-- | net/ipv4/ping.c | 9 | ||||
| -rw-r--r-- | net/ipv4/raw.c | 4 | ||||
| -rw-r--r-- | net/ipv4/udp.c | 7 |
3 files changed, 6 insertions, 14 deletions
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 7d93d62cd5fd..be01d5b8571a 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c | |||
| @@ -626,7 +626,6 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 626 | size_t len, int noblock, int flags, int *addr_len) | 626 | size_t len, int noblock, int flags, int *addr_len) |
| 627 | { | 627 | { |
| 628 | struct inet_sock *isk = inet_sk(sk); | 628 | struct inet_sock *isk = inet_sk(sk); |
| 629 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; | ||
| 630 | struct sk_buff *skb; | 629 | struct sk_buff *skb; |
| 631 | int copied, err; | 630 | int copied, err; |
| 632 | 631 | ||
| @@ -636,9 +635,6 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 636 | if (flags & MSG_OOB) | 635 | if (flags & MSG_OOB) |
| 637 | goto out; | 636 | goto out; |
| 638 | 637 | ||
| 639 | if (addr_len) | ||
| 640 | *addr_len = sizeof(*sin); | ||
| 641 | |||
| 642 | if (flags & MSG_ERRQUEUE) | 638 | if (flags & MSG_ERRQUEUE) |
| 643 | return ip_recv_error(sk, msg, len); | 639 | return ip_recv_error(sk, msg, len); |
| 644 | 640 | ||
| @@ -660,11 +656,14 @@ static int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 660 | sock_recv_timestamp(msg, sk, skb); | 656 | sock_recv_timestamp(msg, sk, skb); |
| 661 | 657 | ||
| 662 | /* Copy the address. */ | 658 | /* Copy the address. */ |
| 663 | if (sin) { | 659 | if (msg->msg_name) { |
| 660 | struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name; | ||
| 661 | |||
| 664 | sin->sin_family = AF_INET; | 662 | sin->sin_family = AF_INET; |
| 665 | sin->sin_port = 0 /* skb->h.uh->source */; | 663 | sin->sin_port = 0 /* skb->h.uh->source */; |
| 666 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 664 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
| 667 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); | 665 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 666 | *addr_len = sizeof(*sin); | ||
| 668 | } | 667 | } |
| 669 | if (isk->cmsg_flags) | 668 | if (isk->cmsg_flags) |
| 670 | ip_cmsg_recv(msg, skb); | 669 | ip_cmsg_recv(msg, skb); |
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6fb233772f79..448e5a77fa88 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c | |||
| @@ -692,9 +692,6 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 692 | if (flags & MSG_OOB) | 692 | if (flags & MSG_OOB) |
| 693 | goto out; | 693 | goto out; |
| 694 | 694 | ||
| 695 | if (addr_len) | ||
| 696 | *addr_len = sizeof(*sin); | ||
| 697 | |||
| 698 | if (flags & MSG_ERRQUEUE) { | 695 | if (flags & MSG_ERRQUEUE) { |
| 699 | err = ip_recv_error(sk, msg, len); | 696 | err = ip_recv_error(sk, msg, len); |
| 700 | goto out; | 697 | goto out; |
| @@ -722,6 +719,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 722 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 719 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
| 723 | sin->sin_port = 0; | 720 | sin->sin_port = 0; |
| 724 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); | 721 | memset(&sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 722 | *addr_len = sizeof(*sin); | ||
| 725 | } | 723 | } |
| 726 | if (inet->cmsg_flags) | 724 | if (inet->cmsg_flags) |
| 727 | ip_cmsg_recv(msg, skb); | 725 | ip_cmsg_recv(msg, skb); |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 93b731d53221..eeb3474daa3f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
| @@ -1207,12 +1207,6 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | |||
| 1207 | int is_udplite = IS_UDPLITE(sk); | 1207 | int is_udplite = IS_UDPLITE(sk); |
| 1208 | bool slow; | 1208 | bool slow; |
| 1209 | 1209 | ||
| 1210 | /* | ||
| 1211 | * Check any passed addresses | ||
| 1212 | */ | ||
| 1213 | if (addr_len) | ||
| 1214 | *addr_len = sizeof(*sin); | ||
| 1215 | |||
| 1216 | if (flags & MSG_ERRQUEUE) | 1210 | if (flags & MSG_ERRQUEUE) |
| 1217 | return ip_recv_error(sk, msg, len); | 1211 | return ip_recv_error(sk, msg, len); |
| 1218 | 1212 | ||
| @@ -1274,6 +1268,7 @@ try_again: | |||
| 1274 | sin->sin_port = udp_hdr(skb)->source; | 1268 | sin->sin_port = udp_hdr(skb)->source; |
| 1275 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; | 1269 | sin->sin_addr.s_addr = ip_hdr(skb)->saddr; |
| 1276 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); | 1270 | memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); |
| 1271 | *addr_len = sizeof(*sin); | ||
| 1277 | } | 1272 | } |
| 1278 | if (inet->cmsg_flags) | 1273 | if (inet->cmsg_flags) |
| 1279 | ip_cmsg_recv(msg, skb); | 1274 | ip_cmsg_recv(msg, skb); |
