]> granicus.if.org Git - strace/commitdiff
net: Fix access beyond tracee buffer for MSG_TRUNC receives
authorJeremy Kerr <jk@ozlabs.org>
Fri, 2 Aug 2019 03:01:29 +0000 (11:01 +0800)
committerDmitry V. Levin <ldv@altlinux.org>
Fri, 2 Aug 2019 16:53:02 +0000 (16:53 +0000)
The recv(), recvfrom() and recvmsg() calls allow a MSG_TRUNC flag, which
indicates that the kernel should return the available size of an
incoming message, rather than the received size.

When strace-ing a truncated recv(), strace will try to access a
return-value size area of the tracee's buffer, which may be larger than
the actual buffer:

  $ obj/strace -e trace=recvfrom ~/tmp/recv-test
  recvfrom(3, "\1\2\3\4\0\0\0\0", 4, MSG_TRUNC, NULL, NULL) = 8

If I add a non-readable guard page after the tracee's recv buffer, we
see strace failing to read the vm area:

  $ obj/strace -e trace=recvfrom ~/tmp/recv-test+guard
  recvfrom(3, obj/strace: umoven: short read (4 < 8) @0x7f0b0d7ddffc
  0x7f0b0d7ddffc, 4, MSG_TRUNC, NULL, NULL) = 8

This change restricts the maximum read size to the size of the tracee's
actual buffer.

The recvmsg() handler will do the right thing by using the .iov_len
data, so no change is required there.

* net.c (sys_recv, sys_recvfrom): Clamp maximum sockbuf size.

net.c

diff --git a/net.c b/net.c
index 1cece9af05098d12e0a94f51e788b6bbcc384c21..b4b3ac40112a881d1b41793d117dea8096a37cb6 100644 (file)
--- a/net.c
+++ b/net.c
@@ -291,7 +291,8 @@ SYS_FUNC(recv)
                        printaddr(tcp->u_arg[1]);
                } else {
                        decode_sockbuf(tcp, tcp->u_arg[0], tcp->u_arg[1],
-                                    tcp->u_rval);
+                                      MIN((kernel_ulong_t) tcp->u_rval,
+                                          tcp->u_arg[2]));
                }
 
                tprintf(", %" PRI_klu ", ", tcp->u_arg[2]);
@@ -316,7 +317,8 @@ SYS_FUNC(recvfrom)
                        printaddr(tcp->u_arg[1]);
                } else {
                        decode_sockbuf(tcp, tcp->u_arg[0], tcp->u_arg[1],
-                                    tcp->u_rval);
+                                      MIN((kernel_ulong_t) tcp->u_rval,
+                                          tcp->u_arg[2]));
                }
                /* size */
                tprintf(", %" PRI_klu ", ", tcp->u_arg[2]);