diff options
author | Andrey Ryabinin <ryabinin.a.a@gmail.com> | 2014-07-26 13:26:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-07-29 15:20:22 -0400 |
commit | 40eea803c6b2cfaab092f053248cbeab3f368412 (patch) | |
tree | a38ce73df90b5e0cc774ffc89e4d93098c598807 | |
parent | 86b7987dd7a8acbaa54a446a73e2431da88b3ca1 (diff) |
net: sendmsg: fix NULL pointer dereference
Sasha's report:
> While fuzzing with trinity inside a KVM tools guest running the latest -next
> kernel with the KASAN patchset, I've stumbled on the following spew:
>
> [ 4448.949424] ==================================================================
> [ 4448.951737] AddressSanitizer: user-memory-access on address 0
> [ 4448.952988] Read of size 2 by thread T19638:
> [ 4448.954510] CPU: 28 PID: 19638 Comm: trinity-c76 Not tainted 3.16.0-rc4-next-20140711-sasha-00046-g07d3099-dirty #813
> [ 4448.956823] ffff88046d86ca40 0000000000000000 ffff880082f37e78 ffff880082f37a40
> [ 4448.958233] ffffffffb6e47068 ffff880082f37a68 ffff880082f37a58 ffffffffb242708d
> [ 4448.959552] 0000000000000000 ffff880082f37a88 ffffffffb24255b1 0000000000000000
> [ 4448.961266] Call Trace:
> [ 4448.963158] dump_stack (lib/dump_stack.c:52)
> [ 4448.964244] kasan_report_user_access (mm/kasan/report.c:184)
> [ 4448.965507] __asan_load2 (mm/kasan/kasan.c:352)
> [ 4448.966482] ? netlink_sendmsg (net/netlink/af_netlink.c:2339)
> [ 4448.967541] netlink_sendmsg (net/netlink/af_netlink.c:2339)
> [ 4448.968537] ? get_parent_ip (kernel/sched/core.c:2555)
> [ 4448.970103] sock_sendmsg (net/socket.c:654)
> [ 4448.971584] ? might_fault (mm/memory.c:3741)
> [ 4448.972526] ? might_fault (./arch/x86/include/asm/current.h:14 mm/memory.c:3740)
> [ 4448.973596] ? verify_iovec (net/core/iovec.c:64)
> [ 4448.974522] ___sys_sendmsg (net/socket.c:2096)
> [ 4448.975797] ? put_lock_stats.isra.13 (./arch/x86/include/asm/preempt.h:98 kernel/locking/lockdep.c:254)
> [ 4448.977030] ? lock_release_holdtime (kernel/locking/lockdep.c:273)
> [ 4448.978197] ? lock_release_non_nested (kernel/locking/lockdep.c:3434 (discriminator 1))
> [ 4448.979346] ? check_chain_key (kernel/locking/lockdep.c:2188)
> [ 4448.980535] __sys_sendmmsg (net/socket.c:2181)
> [ 4448.981592] ? trace_hardirqs_on_caller (kernel/locking/lockdep.c:2600)
> [ 4448.982773] ? trace_hardirqs_on (kernel/locking/lockdep.c:2607)
> [ 4448.984458] ? syscall_trace_enter (arch/x86/kernel/ptrace.c:1500 (discriminator 2))
> [ 4448.985621] ? trace_hardirqs_on_caller (kernel/locking/lockdep.c:2600)
> [ 4448.986754] SyS_sendmmsg (net/socket.c:2201)
> [ 4448.987708] tracesys (arch/x86/kernel/entry_64.S:542)
> [ 4448.988929] ==================================================================
This reports means that we've come to netlink_sendmsg() with msg->msg_name == NULL and msg->msg_namelen > 0.
After this report there was no usual "Unable to handle kernel NULL pointer dereference"
and this gave me a clue that address 0 is mapped and contains valid socket address structure in it.
This bug was introduced in f3d3342602f8bcbf37d7c46641cb9bca7618eb1c
(net: rework recvmsg handler msg_name and msg_namelen logic).
Commit message states that:
"Set msg->msg_name = NULL if user specified a NULL in msg_name but had a
non-null msg_namelen in verify_iovec/verify_compat_iovec. This doesn't
affect sendto as it would bail out earlier while trying to copy-in the
address."
But in fact this affects sendto when address 0 is mapped and contains
socket address structure in it. In such case copy-in address will succeed,
verify_iovec() function will successfully exit with msg->msg_namelen > 0
and msg->msg_name == NULL.
This patch fixes it by setting msg_namelen to 0 if msg_name == NULL.
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: Eric Dumazet <edumazet@google.com>
Cc: <stable@vger.kernel.org>
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Andrey Ryabinin <a.ryabinin@samsung.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/compat.c | 9 | ||||
-rw-r--r-- | net/core/iovec.c | 6 |
2 files changed, 8 insertions, 7 deletions
diff --git a/net/compat.c b/net/compat.c index 9a76eaf63184..bc8aeefddf3f 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -85,7 +85,7 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, | |||
85 | { | 85 | { |
86 | int tot_len; | 86 | int tot_len; |
87 | 87 | ||
88 | if (kern_msg->msg_namelen) { | 88 | if (kern_msg->msg_name && kern_msg->msg_namelen) { |
89 | if (mode == VERIFY_READ) { | 89 | if (mode == VERIFY_READ) { |
90 | int err = move_addr_to_kernel(kern_msg->msg_name, | 90 | int err = move_addr_to_kernel(kern_msg->msg_name, |
91 | kern_msg->msg_namelen, | 91 | kern_msg->msg_namelen, |
@@ -93,10 +93,11 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, | |||
93 | if (err < 0) | 93 | if (err < 0) |
94 | return err; | 94 | return err; |
95 | } | 95 | } |
96 | if (kern_msg->msg_name) | 96 | kern_msg->msg_name = kern_address; |
97 | kern_msg->msg_name = kern_address; | 97 | } else { |
98 | } else | ||
99 | kern_msg->msg_name = NULL; | 98 | kern_msg->msg_name = NULL; |
99 | kern_msg->msg_namelen = 0; | ||
100 | } | ||
100 | 101 | ||
101 | tot_len = iov_from_user_compat_to_kern(kern_iov, | 102 | tot_len = iov_from_user_compat_to_kern(kern_iov, |
102 | (struct compat_iovec __user *)kern_msg->msg_iov, | 103 | (struct compat_iovec __user *)kern_msg->msg_iov, |
diff --git a/net/core/iovec.c b/net/core/iovec.c index 827dd6beb49c..e1ec45ab1e63 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c | |||
@@ -39,7 +39,7 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a | |||
39 | { | 39 | { |
40 | int size, ct, err; | 40 | int size, ct, err; |
41 | 41 | ||
42 | if (m->msg_namelen) { | 42 | if (m->msg_name && m->msg_namelen) { |
43 | if (mode == VERIFY_READ) { | 43 | if (mode == VERIFY_READ) { |
44 | void __user *namep; | 44 | void __user *namep; |
45 | namep = (void __user __force *) m->msg_name; | 45 | namep = (void __user __force *) m->msg_name; |
@@ -48,10 +48,10 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a | |||
48 | if (err < 0) | 48 | if (err < 0) |
49 | return err; | 49 | return err; |
50 | } | 50 | } |
51 | if (m->msg_name) | 51 | m->msg_name = address; |
52 | m->msg_name = address; | ||
53 | } else { | 52 | } else { |
54 | m->msg_name = NULL; | 53 | m->msg_name = NULL; |
54 | m->msg_namelen = 0; | ||
55 | } | 55 | } |
56 | 56 | ||
57 | size = m->msg_iovlen * sizeof(struct iovec); | 57 | size = m->msg_iovlen * sizeof(struct iovec); |