From: Dmitry V. Levin Date: Wed, 26 Aug 2015 12:49:07 +0000 (+0000) Subject: Fix multiple personalities support in parser of io_submit syscall X-Git-Tag: v4.11~251 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=13c217339397de1986210a2d418ffe027c0c2969;p=strace Fix multiple personalities support in parser of io_submit syscall Introduce a new helper function to fetch tracee's long integers and use it to fix multiple personalities support in io_submit parser. * defs.h (umove_long_or_printaddr): New prototype. * util.c (umove_long_or_printaddr): New function. * aio.c (sys_io_submit): Use it to fetch tracee's pointers. * tests/aio.c: New file. * tests/aio.test: New test. * tests/Makefile.am (check_PROGRAMS): Add aio. (TESTS): Add aio.test. * tests/.gitignore: Add aio. --- diff --git a/aio.c b/aio.c index 83bb671f..5822a4fd 100644 --- a/aio.c +++ b/aio.c @@ -109,26 +109,30 @@ SYS_FUNC(io_submit) tprintf("%lu, %ld, [", tcp->u_arg[0], tcp->u_arg[1]); { long i; - struct iocb **iocbs = (void *)tcp->u_arg[2]; -//FIXME: decoding of 32-bit call by 64-bit strace + long iocbs = tcp->u_arg[2]; - for (i = 0; i < nr; i++, iocbs++) { + for (i = 0; i < nr; ++i, iocbs += current_wordsize) { enum iocb_sub sub; - struct iocb *iocbp; + long iocbp; struct iocb iocb; + if (i) tprints(", "); - if (umove_or_printaddr(tcp, (unsigned long)iocbs, &iocbp)) { - /* No point in trying to read iocbs+1 etc */ - /* (nr can be ridiculously large): */ + if (umove_long_or_printaddr(tcp, iocbs, &iocbp)) { + /* + * No point in trying to read the whole array + * because nr can be ridiculously large. + */ break; } + tprints("{"); - if (umove_or_printaddr(tcp, (unsigned long)iocbp, &iocb)) { + if (umove_or_printaddr(tcp, iocbp, &iocb)) { tprints("}"); continue; } + if (iocb.data) { tprints("data="); printaddr((long) iocb.data); diff --git a/defs.h b/defs.h index 20587c71..7f9c3a3e 100644 --- a/defs.h +++ b/defs.h @@ -476,6 +476,7 @@ extern int umoven(struct tcb *, long, unsigned int, void *); extern int umoven_or_printaddr(struct tcb *, long, unsigned int, void *); #define umove_or_printaddr(pid, addr, objp) \ umoven_or_printaddr((pid), (addr), sizeof(*(objp)), (void *) (objp)) +extern int umove_long_or_printaddr(struct tcb *, long, long *); extern int umovestr(struct tcb *, long, unsigned int, char *); extern int upeek(int pid, long, long *); diff --git a/tests/.gitignore b/tests/.gitignore index b272ceb7..6326d40c 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -1,3 +1,4 @@ +aio bpf caps epoll_create1 diff --git a/tests/Makefile.am b/tests/Makefile.am index 0876a46f..9b1703cc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -9,6 +9,7 @@ AM_CPPFLAGS = -I$(top_builddir)/$(OS)/$(ARCH) \ -I$(top_srcdir)/$(OS) check_PROGRAMS = \ + aio \ bpf \ caps \ epoll_create1 \ @@ -78,6 +79,7 @@ TESTS = \ strace-f.test \ qual_syscall.test \ bexecve.test \ + aio.test \ bpf.test \ caps.test \ dumpio.test \ diff --git a/tests/aio.c b/tests/aio.c new file mode 100644 index 00000000..a59ae8ed --- /dev/null +++ b/tests/aio.c @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#if defined __NR_io_setup \ + && defined __NR_io_submit \ + && defined __NR_io_getevents \ + && defined __NR_io_destroy +# include + +int +main(void) +{ + static char data0[4096]; + static char data1[8192]; + + struct iocb cb[2] = { + { + .aio_data = 0x11111111, + .aio_reqprio = 11, + .aio_buf = (unsigned long) data0, + .aio_offset = 0xdefacedfacefeed, + .aio_nbytes = sizeof(data0) + }, + { + .aio_data = 0x22222222, + .aio_reqprio = 22, + .aio_buf = (unsigned long) data1, + .aio_offset = 0xdefacedcafef00d, + .aio_nbytes = sizeof(data1) + } + }; + + long cbs[4] = { + (long) &cb[0], (long) &cb[1], + 0xdeadbeef, 0xbadc0ded + }; + + unsigned long ctx = 0; + const unsigned int nr = sizeof(cb) / sizeof(*cb); + const unsigned long lnr = (unsigned long) (0xdeadbeef00000000ULL | nr); + + struct io_event ev[nr]; + struct timespec ts = { .tv_nsec = 123456789 }; + + (void) close(0); + if (open("/dev/zero", O_RDONLY)) + return 77; + + if (syscall(__NR_io_setup, lnr, &ctx)) + return 77; + printf("io_setup(%u, [%lu]) = 0\n", nr, ctx); + + if (syscall(__NR_io_submit, ctx, nr, cbs) != (long) nr) + return 77; + printf("io_submit(%lu, %u, [" + "{data=%#llx, pread, reqprio=11, filedes=0, " + "buf=%p, nbytes=%u, offset=%lld}, " + "{data=%#llx, pread, reqprio=22, filedes=0, " + "buf=%p, nbytes=%u, offset=%lld}" + "]) = %u\n", + ctx, nr, + (unsigned long long) cb[0].aio_data, data0, + (unsigned int) sizeof(data0), (long long) cb[0].aio_offset, + (unsigned long long) cb[1].aio_data, data1, + (unsigned int) sizeof(data1), (long long) cb[1].aio_offset, + nr); + + if (syscall(__NR_io_getevents, ctx, nr, nr, ev, &ts) != (long) nr) + return 77; + printf("io_getevents(%lu, %u, %u, [" + "{data=%#llx, obj=%p, res=%u, res2=0}, " + "{data=%#llx, obj=%p, res=%u, res2=0}" + "], {0, 123456789}) = %u\n", + ctx, nr, nr, + (unsigned long long) cb[0].aio_data, &cb[0], + (unsigned int) sizeof(data0), + (unsigned long long) cb[1].aio_data, &cb[1], + (unsigned int) sizeof(data1), + nr); + + if (syscall(__NR_io_destroy, ctx)) + return 77; + printf("io_destroy(%lu) = 0\n", ctx); + + puts("+++ exited with 0 +++"); + return 0; +} + +#else + +int +main(void) +{ + return 77; +} + +#endif diff --git a/tests/aio.test b/tests/aio.test new file mode 100755 index 00000000..f5907e46 --- /dev/null +++ b/tests/aio.test @@ -0,0 +1,14 @@ +#!/bin/sh + +# Check io_* syscalls decoding. + +. "${srcdir=.}/init.sh" + +run_prog > /dev/null +OUT="$LOG.out" +syscalls=io_setup,io_submit,io_getevents,io_destroy +run_strace -a14 -e trace=$syscalls $args > "$OUT" +match_diff "$LOG" "$OUT" +rm -f "$OUT" + +exit 0 diff --git a/util.c b/util.c index 94d9265f..d40eafc2 100644 --- a/util.c +++ b/util.c @@ -1132,6 +1132,19 @@ umoven_or_printaddr(struct tcb *tcp, const long addr, const unsigned int len, return 0; } +int +umove_long_or_printaddr(struct tcb *tcp, const long addr, long *ptr) +{ + if (current_wordsize < sizeof(*ptr)) { + uint32_t val32; + int r = umove_or_printaddr(tcp, addr, &val32); + if (!r) + *ptr = (unsigned long) val32; + return r; + } + return umove_or_printaddr(tcp, addr, ptr); +} + /* * Like `umove' but make the additional effort of looking * for a terminating zero byte.