Skip to content

Commit 05a0af6

Browse files
committed
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.
1 parent a541730 commit 05a0af6

File tree

9 files changed

+169
-8
lines changed

9 files changed

+169
-8
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Noteworthy changes in release ?.?? (????-??-??)
1313
* Fixed decoding of mlock2 syscall on sparc.
1414
* Fixed decoding of syscalls unknown to the kernel on s390/s390x.
1515
(addresses Debian bug #485979 and Fedora bug #1298294).
16+
* Fixed decoding and dumping of readv syscall in case of short read.
1617

1718
Noteworthy changes in release 4.11 (2015-12-21)
1819
===============================================

defs.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,9 @@ extern const char *sprintmode(int);
565565
extern const char *sprinttime(time_t);
566566
extern void dumpiov_in_msghdr(struct tcb *, long);
567567
extern void dumpiov_in_mmsghdr(struct tcb *, long);
568-
extern void dumpiov(struct tcb *, int, long);
568+
extern void dumpiov_upto(struct tcb *, int, long, unsigned long);
569+
#define dumpiov(tcp, len, addr) \
570+
dumpiov_upto((tcp), (len), (addr), (unsigned long) -1L)
569571
extern void dumpstr(struct tcb *, long, int);
570572
extern void printstr(struct tcb *, long, long);
571573
extern bool printnum_short(struct tcb *, long, const char *)

io.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ SYS_FUNC(readv)
123123
printfd(tcp, tcp->u_arg[0]);
124124
tprints(", ");
125125
} else {
126-
tprint_iov(tcp, tcp->u_arg[2], tcp->u_arg[1], 1);
126+
tprint_iov_upto(tcp, tcp->u_arg[2], tcp->u_arg[1], 1,
127+
tcp->u_rval);
127128
tprintf(", %lu", tcp->u_arg[2]);
128129
}
129130
return 0;

syscall.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,8 @@ dumpio(struct tcb *tcp)
687687
dumpstr(tcp, tcp->u_arg[1], tcp->u_rval);
688688
return;
689689
case SEN_readv:
690-
dumpiov(tcp, tcp->u_arg[2], tcp->u_arg[1]);
690+
dumpiov_upto(tcp, tcp->u_arg[2], tcp->u_arg[1],
691+
tcp->u_rval);
691692
return;
692693
case SEN_recvmsg:
693694
dumpiov_in_msghdr(tcp, tcp->u_arg[1]);

tests/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pselect6
6767
readdir
6868
readlink
6969
readlinkat
70+
readv
7071
restart_syscall
7172
rt_sigqueueinfo
7273
sched_xetaffinity

tests/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ check_PROGRAMS = \
113113
readdir \
114114
readlink \
115115
readlinkat \
116+
readv \
116117
restart_syscall \
117118
rt_sigqueueinfo \
118119
sched_xetaffinity \
@@ -249,6 +250,7 @@ TESTS = \
249250
readdir.test \
250251
readlink.test \
251252
readlinkat.test \
253+
readv.test \
252254
rt_sigqueueinfo.test \
253255
sched_xetaffinity.test \
254256
sched_xetattr.test \

tests/readv.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Check decoding of readv and writev syscalls.
3+
*
4+
* Copyright (c) 2016 Dmitry V. Levin <[email protected]>
5+
* All rights reserved.
6+
*
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions
9+
* are met:
10+
* 1. Redistributions of source code must retain the above copyright
11+
* notice, this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
* 3. The name of the author may not be used to endorse or promote products
16+
* derived from this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19+
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21+
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23+
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27+
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28+
*/
29+
30+
#include "tests.h"
31+
32+
#include <assert.h>
33+
#include <stdio.h>
34+
#include <unistd.h>
35+
#include <sys/uio.h>
36+
37+
int
38+
main(void)
39+
{
40+
tprintf("%s", "");
41+
42+
int fds[2];
43+
if (pipe(fds))
44+
perror_msg_and_fail("pipe");
45+
assert(0 == fds[0]);
46+
assert(1 == fds[1]);
47+
48+
static const char w0_c[] = "012";
49+
const char *w0_d = hexdump_strdup(w0_c);
50+
void *w0 = tail_memdup(w0_c, LENGTH_OF(w0_c));
51+
52+
static const char w1_c[] = "34567";
53+
const char *w1_d = hexdump_strdup(w1_c);
54+
void *w1 = tail_memdup(w1_c, LENGTH_OF(w1_c));
55+
56+
static const char w2_c[] = "89abcde";
57+
const char *w2_d = hexdump_strdup(w2_c);
58+
void *w2 = tail_memdup(w2_c, LENGTH_OF(w2_c));
59+
60+
static const char r0_c[] = "01234567";
61+
const char *r0_d = hexdump_strdup(r0_c);
62+
static const char r1_c[] = "89abcde";
63+
const char *r1_d = hexdump_strdup(r1_c);
64+
65+
const struct iovec w_iov_[] = {
66+
{
67+
.iov_base = w0,
68+
.iov_len = LENGTH_OF(w0_c)
69+
}, {
70+
.iov_base = w1,
71+
.iov_len = LENGTH_OF(w1_c)
72+
}, {
73+
.iov_base = w2,
74+
.iov_len = LENGTH_OF(w2_c)
75+
}
76+
};
77+
const struct iovec *w_iov = tail_memdup(w_iov_, sizeof(w_iov_));
78+
const unsigned int w_len =
79+
LENGTH_OF(w0_c) + LENGTH_OF(w1_c) + LENGTH_OF(w2_c);
80+
81+
assert(writev(1, w_iov, ARRAY_SIZE(w_iov_)) == (int) w_len);
82+
close(1);
83+
tprintf("writev(1, [{\"%s\", %u}, {\"%s\", %u}"
84+
", {\"%s\", %u}], %u) = %u\n"
85+
" * %u bytes in buffer 0\n"
86+
" | 00000 %-49s %-16s |\n"
87+
" * %u bytes in buffer 1\n"
88+
" | 00000 %-49s %-16s |\n"
89+
" * %u bytes in buffer 2\n"
90+
" | 00000 %-49s %-16s |\n",
91+
w0_c, LENGTH_OF(w0_c), w1_c, LENGTH_OF(w1_c),
92+
w2_c, LENGTH_OF(w2_c), ARRAY_SIZE(w_iov_), w_len,
93+
LENGTH_OF(w0_c), w0_d, w0_c,
94+
LENGTH_OF(w1_c), w1_d, w1_c, LENGTH_OF(w2_c), w2_d, w2_c);
95+
96+
const unsigned int r_len = (w_len + 1) / 2;
97+
void *r0 = tail_alloc(r_len);
98+
const struct iovec r0_iov_[] = {
99+
{
100+
.iov_base = r0,
101+
.iov_len = r_len
102+
}
103+
};
104+
const struct iovec *r_iov = tail_memdup(r0_iov_, sizeof(r0_iov_));
105+
106+
assert(readv(0, r_iov, ARRAY_SIZE(r0_iov_)) == (int) r_len);
107+
tprintf("readv(0, [{\"%s\", %u}], %u) = %u\n"
108+
" * %u bytes in buffer 0\n"
109+
" | 00000 %-49s %-16s |\n",
110+
r0_c, r_len, ARRAY_SIZE(r0_iov_), r_len, r_len, r0_d, r0_c);
111+
112+
void *r1 = tail_alloc(r_len);
113+
void *r2 = tail_alloc(w_len);
114+
const struct iovec r1_iov_[] = {
115+
{
116+
.iov_base = r1,
117+
.iov_len = r_len
118+
},
119+
{
120+
.iov_base = r2,
121+
.iov_len = w_len
122+
}
123+
};
124+
r_iov = tail_memdup(r1_iov_, sizeof(r1_iov_));
125+
126+
assert(readv(0, r_iov, ARRAY_SIZE(r1_iov_)) == (int) w_len - r_len);
127+
tprintf("readv(0, [{\"%s\", %u}, {\"\", %u}], %u) = %u\n"
128+
" * %u bytes in buffer 0\n"
129+
" | 00000 %-49s %-16s |\n",
130+
r1_c, r_len, w_len, ARRAY_SIZE(r1_iov_), w_len - r_len,
131+
w_len - r_len, r1_d, r1_c);
132+
close(0);
133+
134+
tprintf("+++ exited with 0 +++\n");
135+
return 0;
136+
}

tests/readv.test

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/bin/sh
2+
3+
# Check decoding of readv and writev syscalls.
4+
5+
. "${srcdir=.}/init.sh"
6+
7+
run_prog > /dev/null
8+
OUT="$LOG.out"
9+
run_strace -a30 -eread=0 -ewrite=1 -ereadv,writev $args > "$OUT"
10+
match_diff "$LOG" "$OUT"
11+
rm -f "$OUT"
12+
13+
exit 0

util.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ printstr(struct tcb *tcp, long addr, long len)
841841
}
842842

843843
void
844-
dumpiov(struct tcb *tcp, int len, long addr)
844+
dumpiov_upto(struct tcb *tcp, int len, long addr, unsigned long data_size)
845845
{
846846
#if SUPPORTED_PERSONALITIES > 1
847847
union {
@@ -873,12 +873,16 @@ dumpiov(struct tcb *tcp, int len, long addr)
873873
}
874874
if (umoven(tcp, addr, size, iov) >= 0) {
875875
for (i = 0; i < len; i++) {
876+
unsigned long iov_len = iov_iov_len(i);
877+
if (iov_len > data_size)
878+
iov_len = data_size;
879+
if (!iov_len)
880+
break;
881+
data_size -= iov_len;
876882
/* include the buffer number to make it easy to
877883
* match up the trace with the source */
878-
tprintf(" * %lu bytes in buffer %d\n",
879-
(unsigned long)iov_iov_len(i), i);
880-
dumpstr(tcp, (long) iov_iov_base(i),
881-
iov_iov_len(i));
884+
tprintf(" * %lu bytes in buffer %d\n", iov_len, i);
885+
dumpstr(tcp, (long) iov_iov_base(i), iov_len);
882886
}
883887
}
884888
free(iov);

0 commit comments

Comments
 (0)