From: Dmitry V. Levin Date: Wed, 20 Jan 2016 00:17:02 +0000 (+0000) Subject: Fix decoding and dumping of readv syscall in case of short read X-Git-Tag: v4.12~622 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=05a0af6d6011e82ff4a88de468d3c21338461d75;p=strace Fix decoding and dumping of readv syscall in case of short read * defs.h (dumpiov_upto): New prototype. (dumpiov): Change to a wrapper around dumpiov_upto. * util.c (dumpiov): Rename to dumpiov_upto, add and check data_size argument. * io.c (SYS_FUNC(readv)): Call tprint_iov_upto instead of tprint_iov and specify syscall return value as a data size limit. * syscall.c (dumpio): In case of SEN_readv, call dumpiov_upto instead of dumpiov and specify syscall return value as a data size limit. * NEWS: Mention this fix. * tests/readv.c: New file. * tests/readv.test: New test. * tests/Makefile.am (check_PROGRAMS): Add readv. (TESTS): Add readv.test. * tests/.gitignore: Add readv. --- diff --git a/NEWS b/NEWS index 33fb3b08..8b09bdae 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ Noteworthy changes in release ?.?? (????-??-??) * Fixed decoding of mlock2 syscall on sparc. * Fixed decoding of syscalls unknown to the kernel on s390/s390x. (addresses Debian bug #485979 and Fedora bug #1298294). + * Fixed decoding and dumping of readv syscall in case of short read. Noteworthy changes in release 4.11 (2015-12-21) =============================================== diff --git a/defs.h b/defs.h index bae212c6..47ca0c05 100644 --- a/defs.h +++ b/defs.h @@ -565,7 +565,9 @@ extern const char *sprintmode(int); extern const char *sprinttime(time_t); extern void dumpiov_in_msghdr(struct tcb *, long); extern void dumpiov_in_mmsghdr(struct tcb *, long); -extern void dumpiov(struct tcb *, int, long); +extern void dumpiov_upto(struct tcb *, int, long, unsigned long); +#define dumpiov(tcp, len, addr) \ + dumpiov_upto((tcp), (len), (addr), (unsigned long) -1L) extern void dumpstr(struct tcb *, long, int); extern void printstr(struct tcb *, long, long); extern bool printnum_short(struct tcb *, long, const char *) diff --git a/io.c b/io.c index 5ee967c1..b98bc8a1 100644 --- a/io.c +++ b/io.c @@ -123,7 +123,8 @@ SYS_FUNC(readv) printfd(tcp, tcp->u_arg[0]); tprints(", "); } else { - tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], 1); + tprint_iov_upto(tcp, tcp->u_arg[2], tcp->u_arg[1], 1, + tcp->u_rval); tprintf(", %lu", tcp->u_arg[2]); } return 0; diff --git a/syscall.c b/syscall.c index c5aceed4..5f7b0edc 100644 --- a/syscall.c +++ b/syscall.c @@ -687,7 +687,8 @@ dumpio(struct tcb *tcp) dumpstr(tcp, tcp->u_arg[1], tcp->u_rval); return; case SEN_readv: - dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]); + dumpiov_upto(tcp, tcp->u_arg[2], tcp->u_arg[1], + tcp->u_rval); return; case SEN_recvmsg: dumpiov_in_msghdr(tcp, tcp->u_arg[1]); diff --git a/tests/.gitignore b/tests/.gitignore index 6188bddd..6b62117e 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -67,6 +67,7 @@ pselect6 readdir readlink readlinkat +readv restart_syscall rt_sigqueueinfo sched_xetaffinity diff --git a/tests/Makefile.am b/tests/Makefile.am index cf4e673b..d683a6bc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -113,6 +113,7 @@ check_PROGRAMS = \ readdir \ readlink \ readlinkat \ + readv \ restart_syscall \ rt_sigqueueinfo \ sched_xetaffinity \ @@ -249,6 +250,7 @@ TESTS = \ readdir.test \ readlink.test \ readlinkat.test \ + readv.test \ rt_sigqueueinfo.test \ sched_xetaffinity.test \ sched_xetattr.test \ diff --git a/tests/readv.c b/tests/readv.c new file mode 100644 index 00000000..ec35c8ae --- /dev/null +++ b/tests/readv.c @@ -0,0 +1,136 @@ +/* + * Check decoding of readv and writev syscalls. + * + * Copyright (c) 2016 Dmitry V. Levin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "tests.h" + +#include +#include +#include +#include + +int +main(void) +{ + tprintf("%s", ""); + + int fds[2]; + if (pipe(fds)) + perror_msg_and_fail("pipe"); + assert(0 == fds[0]); + assert(1 == fds[1]); + + static const char w0_c[] = "012"; + const char *w0_d = hexdump_strdup(w0_c); + void *w0 = tail_memdup(w0_c, LENGTH_OF(w0_c)); + + static const char w1_c[] = "34567"; + const char *w1_d = hexdump_strdup(w1_c); + void *w1 = tail_memdup(w1_c, LENGTH_OF(w1_c)); + + static const char w2_c[] = "89abcde"; + const char *w2_d = hexdump_strdup(w2_c); + void *w2 = tail_memdup(w2_c, LENGTH_OF(w2_c)); + + static const char r0_c[] = "01234567"; + const char *r0_d = hexdump_strdup(r0_c); + static const char r1_c[] = "89abcde"; + const char *r1_d = hexdump_strdup(r1_c); + + const struct iovec w_iov_[] = { + { + .iov_base = w0, + .iov_len = LENGTH_OF(w0_c) + }, { + .iov_base = w1, + .iov_len = LENGTH_OF(w1_c) + }, { + .iov_base = w2, + .iov_len = LENGTH_OF(w2_c) + } + }; + const struct iovec *w_iov = tail_memdup(w_iov_, sizeof(w_iov_)); + const unsigned int w_len = + LENGTH_OF(w0_c) + LENGTH_OF(w1_c) + LENGTH_OF(w2_c); + + assert(writev(1, w_iov, ARRAY_SIZE(w_iov_)) == (int) w_len); + close(1); + tprintf("writev(1, [{\"%s\", %u}, {\"%s\", %u}" + ", {\"%s\", %u}], %u) = %u\n" + " * %u bytes in buffer 0\n" + " | 00000 %-49s %-16s |\n" + " * %u bytes in buffer 1\n" + " | 00000 %-49s %-16s |\n" + " * %u bytes in buffer 2\n" + " | 00000 %-49s %-16s |\n", + w0_c, LENGTH_OF(w0_c), w1_c, LENGTH_OF(w1_c), + w2_c, LENGTH_OF(w2_c), ARRAY_SIZE(w_iov_), w_len, + LENGTH_OF(w0_c), w0_d, w0_c, + LENGTH_OF(w1_c), w1_d, w1_c, LENGTH_OF(w2_c), w2_d, w2_c); + + const unsigned int r_len = (w_len + 1) / 2; + void *r0 = tail_alloc(r_len); + const struct iovec r0_iov_[] = { + { + .iov_base = r0, + .iov_len = r_len + } + }; + const struct iovec *r_iov = tail_memdup(r0_iov_, sizeof(r0_iov_)); + + assert(readv(0, r_iov, ARRAY_SIZE(r0_iov_)) == (int) r_len); + tprintf("readv(0, [{\"%s\", %u}], %u) = %u\n" + " * %u bytes in buffer 0\n" + " | 00000 %-49s %-16s |\n", + r0_c, r_len, ARRAY_SIZE(r0_iov_), r_len, r_len, r0_d, r0_c); + + void *r1 = tail_alloc(r_len); + void *r2 = tail_alloc(w_len); + const struct iovec r1_iov_[] = { + { + .iov_base = r1, + .iov_len = r_len + }, + { + .iov_base = r2, + .iov_len = w_len + } + }; + r_iov = tail_memdup(r1_iov_, sizeof(r1_iov_)); + + assert(readv(0, r_iov, ARRAY_SIZE(r1_iov_)) == (int) w_len - r_len); + tprintf("readv(0, [{\"%s\", %u}, {\"\", %u}], %u) = %u\n" + " * %u bytes in buffer 0\n" + " | 00000 %-49s %-16s |\n", + r1_c, r_len, w_len, ARRAY_SIZE(r1_iov_), w_len - r_len, + w_len - r_len, r1_d, r1_c); + close(0); + + tprintf("+++ exited with 0 +++\n"); + return 0; +} diff --git a/tests/readv.test b/tests/readv.test new file mode 100755 index 00000000..6a071e38 --- /dev/null +++ b/tests/readv.test @@ -0,0 +1,13 @@ +#!/bin/sh + +# Check decoding of readv and writev syscalls. + +. "${srcdir=.}/init.sh" + +run_prog > /dev/null +OUT="$LOG.out" +run_strace -a30 -eread=0 -ewrite=1 -ereadv,writev $args > "$OUT" +match_diff "$LOG" "$OUT" +rm -f "$OUT" + +exit 0 diff --git a/util.c b/util.c index e6985f88..01969a7f 100644 --- a/util.c +++ b/util.c @@ -841,7 +841,7 @@ printstr(struct tcb *tcp, long addr, long len) } void -dumpiov(struct tcb *tcp, int len, long addr) +dumpiov_upto(struct tcb *tcp, int len, long addr, unsigned long data_size) { #if SUPPORTED_PERSONALITIES > 1 union { @@ -873,12 +873,16 @@ dumpiov(struct tcb *tcp, int len, long addr) } if (umoven(tcp, addr, size, iov) >= 0) { for (i = 0; i < len; i++) { + unsigned long iov_len = iov_iov_len(i); + if (iov_len > data_size) + iov_len = data_size; + if (!iov_len) + break; + data_size -= iov_len; /* include the buffer number to make it easy to * match up the trace with the source */ - tprintf(" * %lu bytes in buffer %d\n", - (unsigned long)iov_iov_len(i), i); - dumpstr(tcp, (long) iov_iov_base(i), - iov_iov_len(i)); + tprintf(" * %lu bytes in buffer %d\n", iov_len, i); + dumpstr(tcp, (long) iov_iov_base(i), iov_len); } } free(iov);