2016-10-15 03:20:06 +03:00
/*
* Check decoding of process_vm_readv / process_vm_writev syscall .
*
* Copyright ( c ) 2016 Eugene Syromyatnikov < evgsyr @ gmail . com >
2018-06-14 11:00:00 +00:00
* Copyright ( c ) 2016 - 2018 The strace developers .
2016-10-15 03:20:06 +03:00
* 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 <inttypes.h>
# include <stdio.h>
# include <unistd.h>
# include <sys/uio.h>
# if OP_WR
# define in_iovec rmt_iovec
# define out_iovec lcl_iovec
# define in_iov rmt_iov
# define out_iov lcl_iov
# else
# define in_iovec lcl_iovec
# define out_iovec rmt_iovec
# define in_iov lcl_iov
# define out_iov rmt_iov
# endif
typedef void ( * iov_print_fn ) ( const struct iovec * , const void * , long ) ;
enum { MAX_SEGM_COUNT = 2 , MAX_STR_LEN = 5 } ;
struct print_iov_arg {
uint32_t count ;
uint32_t valid : 1 ,
2017-06-17 22:23:09 +00:00
string : 1 ,
2016-10-15 03:20:06 +03:00
addr_term : 1 ,
check_rc : 1 ;
uint32_t str_segms ;
uint8_t str_base [ MAX_SEGM_COUNT ] ;
uint8_t str_size [ MAX_SEGM_COUNT ] ;
} ;
static void
print_iov ( const struct iovec * iov , const void * arg_ptr , long rc )
{
const struct print_iov_arg * arg = arg_ptr ;
uint32_t i ;
uint32_t num_segm = 0 ;
uint64_t segm_offs = 0 ;
if ( ! arg | | ! arg - > valid ) {
if ( iov )
printf ( " %p " , iov ) ;
else
printf ( " NULL " ) ;
return ;
}
printf ( " [ " ) ;
for ( i = 0 ; i < arg - > count ; i + + ) {
if ( i )
printf ( " , " ) ;
if ( i > = MAX_STR_LEN ) {
printf ( " ... " ) ;
break ;
}
printf ( " {iov_base= " ) ;
if ( arg - > string & & ( ! arg - > check_rc | | ( rc ! = - 1 ) ) ) {
uint64_t str_left = iov [ i ] . iov_len ;
uint64_t pr_count = 0 ;
printf ( " \" " ) ;
while ( str_left - - ) {
static const char oct_str [ ] = " 01234567 " ;
uint8_t c = arg - > str_base [ num_segm ] + segm_offs ;
if ( ( num_segm > = arg - > str_segms ) | |
( num_segm > = MAX_SEGM_COUNT ) )
error_msg_and_fail ( " print_iov: segment "
" count overrun " ) ;
if ( pr_count + + < MAX_STR_LEN )
printf ( " \\ %.1s%.1s%d " ,
( c > > 6 ) ?
oct_str + ( c > > 6 ) : " " ,
( c > > 3 ) ?
oct_str + ( ( c > > 3 ) & 7 ) : " " ,
c & 7 ) ;
segm_offs + + ;
if ( segm_offs > = arg - > str_size [ num_segm ] ) {
num_segm + + ;
segm_offs = 0 ;
}
}
printf ( " \" " ) ;
if ( pr_count > MAX_STR_LEN )
printf ( " ... " ) ;
} else {
if ( iov [ i ] . iov_base )
printf ( " %p " , iov [ i ] . iov_base ) ;
else
printf ( " NULL " ) ;
}
printf ( " , iov_len=%zu} " , iov [ i ] . iov_len ) ;
}
if ( arg - > addr_term )
print_array: enhance printing of unfetchable object addresses
When umoven_func invocation fails to fetch data, it prints the faulty
address. If this happens to a subsequent umoven_func invocation,
the printed address may be undistinguishable from a valid data printed
by print_func, e.g. when the data is printed in a numeric form like
[0x1, 0x2, 0x3, 0xdefaced].
Fix this source of confusion by moving the printing of the faulty
address from umoven_func to print_array itself. This change renames
umoven_func to tfetch_mem_func and changes its semantics, so that
- tfetch_mem_func never prints anything;
- tfetch_mem_func returns true if the fetch succeeded,
and false otherwise.
* defs.h (print_array): Replace umoven_func argument with
tfetch_mem_func.
* util.c (print_array): Replace umoven_func argument with
tfetch_mem_func, document expected tfetch_mem_func return value
semantics. When tfetch_mem_func returns false, print either addr
or "... /* addr */" depending on the context (inside the array or not).
* bpf.c (print_ebpf_prog, print_bpf_prog_info,
BEGIN_BPF_CMD_DECODER(BPF_PROG_QUERY)): Replace umoven_or_printaddr
argument of print_array with tfetch_mem.
* bpf_filter.c (print_bpf_fprog): Likewise.
* btrfs.c (btrfs_print_logical_ino_container,
btrfs_print_ino_path_container, btrfs_print_qgroup_inherit,
btrfs_ioctl): Likewise.
* dm.c (dm_decode_dm_target_deps): Likewise.
* epoll.c (epoll_wait_common): Likewise.
* file_ioctl.c (file_ioctl): Likewise.
* ipc_sem.c (tprint_sembuf_array): Likewise.
* kexec.c (print_kexec_segments): Likewise.
* mem.c (SYS_FUNC(subpage_prot)): Likewise.
* net.c (print_getsockopt): Likewise.
* netlink.c (decode_nlmsgerr_attr_cookie): Likewise.
* netlink_netlink_diag.c (decode_netlink_diag_groups): Likewise.
* netlink_packet_diag.c (decode_packet_diag_mclist): Likewise.
* netlink_unix_diag.c (decode_unix_diag_inode): Likewise.
* nlattr.c (decode_nla_meminfo): Likewise.
* numa.c (print_nodemask, SYS_FUNC(move_pages),
* perf_ioctl.c (perf_ioctl_query_bpf): Likewise.
* poll.c (decode_poll_entering): Likewise.
* printsiginfo.c (print_siginfo_array): Likewise.
* rtnl_tc.c (decode_tca_stab_data): Likewise.
* sock.c (decode_ifconf): Likewise.
* uid.c (print_groups): Likewise.
* io.c (SYS_FUNC(io_submit), SYS_FUNC(io_getevents)): Replace
umoven_or_printaddr argument of print_array with tfetch_mem.
(tprint_iov_upto): Replace umoven_or_printaddr_ignore_syserror
with tfetch_mem_ignore_syserror.
* v4l2.c (print_v4l2_format_fmt): Replace umoven_or_printaddr argument
of print_array with tfetch_mem.
(print_v4l2_ext_controls): Replace umoven_or_printaddr_ignore_syserror
with tfetch_mem_ignore_syserror.
* mmsghdr.c (fetch_struct_mmsghdr_or_printaddr): Rename
to fetch_struct_mmsghdr_for_print, do not print address, return bool.
(decode_mmsgvec): Replace fetch_struct_mmsghdr_or_printaddr
with fetch_struct_mmsghdr_for_print.
* tests/aio.c (main): Update expected output.
* tests/bpf.c (print_BPF_PROG_QUERY_attr5): Likewise.
* tests/ioctl_perf-success.c (main): Likewise.
* tests/ioctl_v4l2.c (main): Update expected output.
* tests/kexec_load.c (main): Likewise.
* tests/mmsg_name.c (test_mmsg_name): Update expected output.
* tests/move_pages.c (print_page_array, print_node_array): Likewise.
* tests/poll.c (print_pollfd_array_entering): Likewise.
* tests/preadv-pwritev.c (main): Likewise.
* tests/preadv2-pwritev2.c (dumpio): Likewise.
* tests/process_vm_readv_writev.c (print_iov): Likewise.
* tests/pwritev.c (print_iovec): Likewise.
* tests/readv.c (main): Likewise.
* tests/seccomp-filter-v.c
* tests/semop.c (main): Likewise.
* tests/set_mempolicy.c (print_nodes): Likewise.
* tests/setgroups.c (main): Likewise.
* tests/test_nlattr.h (print_nlattr) Likewise.
Co-Authored-by: Eugene Syromyatnikov <evgsyr@gmail.com>
2018-05-29 01:15:19 +00:00
printf ( " , ... /* %p */ " , iov + arg - > count ) ;
2016-10-15 03:20:06 +03:00
printf ( " ] " ) ;
}
static void
do_call ( kernel_ulong_t pid ,
kernel_ulong_t local_iov , const char * local_arg ,
kernel_ulong_t liovcnt ,
kernel_ulong_t remote_iov , const char * remote_arg ,
kernel_ulong_t riovcnt ,
kernel_ulong_t flags , iov_print_fn pr_iov )
{
long rc ;
const char * errstr ;
rc = syscall ( OP_NR , pid , local_iov , liovcnt , remote_iov , riovcnt ,
flags ) ;
errstr = sprintrc ( rc ) ;
printf ( " %s(%d, " , OP_STR , ( int ) pid ) ;
if ( pr_iov )
pr_iov ( ( const struct iovec * ) ( uintptr_t ) local_iov , local_arg ,
rc ) ;
else
printf ( " %s " , local_arg ) ;
printf ( " , %lu, " , ( unsigned long ) liovcnt ) ;
if ( pr_iov )
pr_iov ( ( const struct iovec * ) ( uintptr_t ) remote_iov ,
remote_arg , rc ) ;
else
printf ( " %s " , remote_arg ) ;
printf ( " , %lu, %lu) = %s \n " , ( unsigned long ) riovcnt ,
( unsigned long ) flags , errstr ) ;
}
kernel_ulong_t
ptr_cast ( void * ptr )
{
return ( kernel_ulong_t ) ( uintptr_t ) ptr ;
}
int
main ( void )
{
enum {
SIZE_11 = 2 ,
SIZE_12 = 3 ,
SIZE_13 = 4 ,
SIZE_1 = SIZE_11 + SIZE_12 + SIZE_13 ,
SIZE_21 = 5 ,
SIZE_22 = 6 ,
SIZE_23 = 7 ,
SIZE_2 = SIZE_21 + SIZE_22 + SIZE_23 ,
} ;
enum {
SEGM1_BASE = 0x80 ,
SEGM2_BASE = 0xA0 ,
} ;
static const kernel_ulong_t bogus_pid =
( kernel_ulong_t ) 0xbadfaceddeadca57ULL ;
static const kernel_ulong_t bogus_iovcnt1 =
( kernel_ulong_t ) 0xdec0ded1defaced2ULL ;
static const kernel_ulong_t bogus_iovcnt2 =
( kernel_ulong_t ) 0xdec0ded3defaced4ULL ;
static const kernel_ulong_t bogus_flags =
( kernel_ulong_t ) 0xdeadc0deda7adeadULL ;
pid_t my_pid = getpid ( ) ;
char * data1_out = tail_alloc ( SIZE_1 ) ;
char * data2_out = tail_alloc ( SIZE_2 ) ;
char * data1_in = tail_alloc ( SIZE_2 ) ;
char * data2_in = tail_alloc ( SIZE_1 ) ;
struct iovec bogus_iovec [ ] = {
{ data1_out + SIZE_1 , ( size_t ) 0xdeadfaceca57beefULL } ,
{ data1_in + SIZE_2 , ( size_t ) 0xbadc0dedda7adeadULL } ,
{ data2_out + SIZE_2 , ( size_t ) 0xf157facedec0ded1ULL } ,
{ data2_in + SIZE_1 , ( size_t ) 0xdefaced2bea7be57ULL } ,
} ;
struct iovec out_iovec [ ] = {
{ data1_out , SIZE_11 } ,
{ data1_out + SIZE_11 , SIZE_12 } ,
{ data1_out + SIZE_11 + SIZE_12 , SIZE_13 } ,
{ data2_out , SIZE_21 } ,
{ data2_out + SIZE_21 , SIZE_22 } ,
{ data2_out + SIZE_21 + SIZE_22 , SIZE_23 } ,
} ;
struct iovec in_iovec [ ] = {
{ data1_in , SIZE_23 } ,
{ data1_in + SIZE_23 , SIZE_22 } ,
{ data1_in + SIZE_23 + SIZE_22 , SIZE_21 } ,
{ data2_in , SIZE_13 } ,
{ data2_in + SIZE_13 , SIZE_12 } ,
{ data2_in + SIZE_13 + SIZE_12 , SIZE_11 } ,
} ;
struct iovec * bogus_iov = tail_memdup ( bogus_iovec , sizeof ( bogus_iovec ) ) ;
struct iovec * lcl_iov = tail_memdup ( lcl_iovec , sizeof ( lcl_iovec ) ) ;
struct iovec * rmt_iov = tail_memdup ( rmt_iovec , sizeof ( rmt_iovec ) ) ;
struct print_iov_arg bogus_arg = { ARRAY_SIZE ( bogus_iovec ) , 1 } ;
struct print_iov_arg lcl_arg = { ARRAY_SIZE ( lcl_iovec ) , 1 , 1 , 0 , 0 ,
2 , { SEGM1_BASE , SEGM2_BASE } , { SIZE_1 , SIZE_2 } } ;
struct print_iov_arg rmt_arg = { ARRAY_SIZE ( rmt_iovec ) , 1 } ;
2017-06-17 22:23:09 +00:00
struct print_iov_arg bogus_arg_cut = {
ARRAY_SIZE ( bogus_iovec ) - 2 , 1 , 0 , 1
} ;
struct print_iov_arg lcl_arg_cut = {
ARRAY_SIZE ( lcl_iovec ) - 2 , 1 , 1 , 1 , 0 , 2 ,
{ SEGM1_BASE + SIZE_11 + SIZE_12 , SEGM2_BASE } ,
{ SIZE_13 , SIZE_2 }
} ;
struct print_iov_arg rmt_arg_cut = { ARRAY_SIZE ( rmt_iovec ) - 2 , 1 } ;
2016-10-15 03:20:06 +03:00
fill_memory_ex ( data1_out , SIZE_1 , SEGM1_BASE , SIZE_1 ) ;
fill_memory_ex ( data2_out , SIZE_2 , SEGM2_BASE , SIZE_2 ) ;
do_call ( bogus_pid , ( kernel_ulong_t ) ( uintptr_t ) ARG_STR ( NULL ) ,
bogus_iovcnt1 , ( kernel_ulong_t ) ( uintptr_t ) ARG_STR ( NULL ) ,
bogus_iovcnt2 , bogus_flags , NULL ) ;
do_call ( my_pid , ptr_cast ( bogus_iov + ARRAY_SIZE ( bogus_iovec ) ) ,
" [] " , 0 , ptr_cast ( in_iov + ARRAY_SIZE ( in_iovec ) ) , " [] " ,
0 , 0 , NULL ) ;
do_call ( my_pid , ptr_cast ( bogus_iov + ARRAY_SIZE ( bogus_iovec ) ) , NULL ,
bogus_iovcnt1 , ptr_cast ( in_iov + ARRAY_SIZE ( in_iovec ) ) , NULL ,
bogus_iovcnt2 , 0 , print_iov ) ;
do_call ( my_pid , ptr_cast ( bogus_iov ) , ( char * ) & bogus_arg ,
ARRAY_SIZE ( bogus_iovec ) , ptr_cast ( rmt_iov + 2 ) ,
( char * ) & rmt_arg_cut , ARRAY_SIZE ( rmt_iovec ) - 2 , 0 , print_iov ) ;
# if !OP_WR
lcl_arg . check_rc = 1 ;
lcl_arg_cut . check_rc = 1 ;
# endif
do_call ( my_pid , ptr_cast ( lcl_iov + 2 ) , ( char * ) & lcl_arg_cut ,
ARRAY_SIZE ( lcl_iovec ) - 1 , ptr_cast ( bogus_iov + 2 ) ,
( char * ) & bogus_arg_cut , ARRAY_SIZE ( bogus_iovec ) - 1 , 0 ,
print_iov ) ;
lcl_arg_cut . addr_term = 0 ;
rmt_arg_cut . addr_term = 1 ;
rmt_arg_cut . count = 5 ;
do_call ( my_pid , ptr_cast ( lcl_iov + 2 ) , ( char * ) & lcl_arg_cut ,
ARRAY_SIZE ( lcl_iovec ) - 2 , ptr_cast ( rmt_iov + 1 ) ,
( char * ) & rmt_arg_cut , ARRAY_SIZE ( rmt_iovec ) , 0 , print_iov ) ;
/* Correct call */
do_call ( my_pid , ptr_cast ( lcl_iov ) , ( char * ) & lcl_arg ,
ARRAY_SIZE ( lcl_iovec ) , ptr_cast ( rmt_iov ) , ( char * ) & rmt_arg ,
ARRAY_SIZE ( rmt_iovec ) , 0 , print_iov ) ;
puts ( " +++ exited with 0 +++ " ) ;
return 0 ;
}