/*--------------------------------------------------------------------*/ /*--- Linux-specific syscalls, etc. syswrap-linux.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2005 Nicholas Nethercote njn@valgrind.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "pub_core_basics.h" #include "pub_core_threadstate.h" #include "pub_core_debuginfo.h" // Needed for pub_core_aspacemgr :( #include "pub_core_aspacemgr.h" #include "pub_core_debuglog.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcfile.h" #include "pub_core_libcprint.h" #include "pub_core_libcproc.h" #include "pub_core_mallocfree.h" #include "pub_core_tooliface.h" #include "pub_core_options.h" #include "pub_core_scheduler.h" #include "pub_core_signals.h" #include "pub_core_syscall.h" #include "priv_types_n_macros.h" #include "priv_syswrap-generic.h" #include "priv_syswrap-linux.h" // Run a thread from beginning to end and return the thread's // scheduler-return-code. VgSchedReturnCode ML_(thread_wrapper)(Word /*ThreadId*/ tidW) { VG_(debugLog)(1, "core_os", "ML_(thread_wrapper)(tid=%lld): entry\n", (ULong)tidW); VgSchedReturnCode ret; ThreadId tid = (ThreadId)tidW; ThreadState* tst = VG_(get_ThreadState)(tid); vg_assert(tst->status == VgTs_Init); /* make sure we get the CPU lock before doing anything significant */ VG_(set_running)(tid); if (0) VG_(printf)("thread tid %d started: stack = %p\n", tid, &tid); VG_TRACK ( post_thread_create, tst->os_state.parent, tid ); tst->os_state.lwpid = VG_(gettid)(); tst->os_state.threadgroup = VG_(getpid)(); /* Thread created with all signals blocked; scheduler will set the appropriate mask */ ret = VG_(scheduler)(tid); vg_assert(VG_(is_exiting)(tid)); vg_assert(tst->status == VgTs_Runnable); vg_assert(VG_(is_running_thread)(tid)); VG_(debugLog)(1, "core_os", "ML_(thread_wrapper)(tid=%lld): done\n", (ULong)tidW); /* Return to caller, still holding the lock. */ return ret; } /* --------------------------------------------------------------------- PRE/POST wrappers for arch-generic, Linux-specific syscalls ------------------------------------------------------------------ */ // Nb: See the comment above the generic PRE/POST wrappers in // m_syswrap/syswrap-generic.c for notes about how they work. #define PRE(name) DEFN_PRE_TEMPLATE(linux, name) #define POST(name) DEFN_POST_TEMPLATE(linux, name) // Combine two 32-bit values into a 64-bit value #define LOHI64(lo,hi) ( (lo) | ((ULong)(hi) << 32) ) /* --------------------------------------------------------------------- *mount wrappers ------------------------------------------------------------------ */ PRE(sys_mount) { // Nb: depending on 'flags', the 'type' and 'data' args may be ignored. // We are conservative and check everything, except the memory pointed to // by 'data'. *flags |= SfMayBlock; PRINT( "sys_mount( %p, %p, %p, %p, %p )" ,ARG1,ARG2,ARG3,ARG4,ARG5); PRE_REG_READ5(long, "mount", char *, source, char *, target, char *, type, unsigned long, flags, void *, data); PRE_MEM_RASCIIZ( "mount(source)", ARG1); PRE_MEM_RASCIIZ( "mount(target)", ARG2); PRE_MEM_RASCIIZ( "mount(type)", ARG3); } PRE(sys_oldumount) { PRINT("sys_oldumount( %p )", ARG1); PRE_REG_READ1(long, "umount", char *, path); PRE_MEM_RASCIIZ( "umount(path)", ARG1); } PRE(sys_umount) { PRINT("sys_umount( %p, %d )", ARG1, ARG2); PRE_REG_READ2(long, "umount2", char *, path, int, flags); PRE_MEM_RASCIIZ( "umount2(path)", ARG1); } /* --------------------------------------------------------------------- 16- and 32-bit uid/gid wrappers ------------------------------------------------------------------ */ PRE(sys_setfsuid16) { PRINT("sys_setfsuid16 ( %d )", ARG1); PRE_REG_READ1(long, "setfsuid16", vki_old_uid_t, uid); } PRE(sys_setfsuid) { PRINT("sys_setfsuid ( %d )", ARG1); PRE_REG_READ1(long, "setfsuid", vki_uid_t, uid); } PRE(sys_setfsgid16) { PRINT("sys_setfsgid16 ( %d )", ARG1); PRE_REG_READ1(long, "setfsgid16", vki_old_gid_t, gid); } PRE(sys_setfsgid) { PRINT("sys_setfsgid ( %d )", ARG1); PRE_REG_READ1(long, "setfsgid", vki_gid_t, gid); } PRE(sys_setresuid16) { PRINT("sys_setresuid16 ( %d, %d, %d )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "setresuid16", vki_old_uid_t, ruid, vki_old_uid_t, euid, vki_old_uid_t, suid); } PRE(sys_setresuid) { PRINT("sys_setresuid ( %d, %d, %d )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "setresuid", vki_uid_t, ruid, vki_uid_t, euid, vki_uid_t, suid); } PRE(sys_getresuid16) { PRINT("sys_getresuid16 ( %p, %p, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "getresuid16", vki_old_uid_t *, ruid, vki_old_uid_t *, euid, vki_old_uid_t *, suid); PRE_MEM_WRITE( "getresuid16(ruid)", ARG1, sizeof(vki_old_uid_t) ); PRE_MEM_WRITE( "getresuid16(euid)", ARG2, sizeof(vki_old_uid_t) ); PRE_MEM_WRITE( "getresuid16(suid)", ARG3, sizeof(vki_old_uid_t) ); } POST(sys_getresuid16) { vg_assert(SUCCESS); if (RES == 0) { POST_MEM_WRITE( ARG1, sizeof(vki_old_uid_t) ); POST_MEM_WRITE( ARG2, sizeof(vki_old_uid_t) ); POST_MEM_WRITE( ARG3, sizeof(vki_old_uid_t) ); } } PRE(sys_getresuid) { PRINT("sys_getresuid ( %p, %p, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "getresuid", vki_uid_t *, ruid, vki_uid_t *, euid, vki_uid_t *, suid); PRE_MEM_WRITE( "getresuid(ruid)", ARG1, sizeof(vki_uid_t) ); PRE_MEM_WRITE( "getresuid(euid)", ARG2, sizeof(vki_uid_t) ); PRE_MEM_WRITE( "getresuid(suid)", ARG3, sizeof(vki_uid_t) ); } POST(sys_getresuid) { vg_assert(SUCCESS); if (RES == 0) { POST_MEM_WRITE( ARG1, sizeof(vki_uid_t) ); POST_MEM_WRITE( ARG2, sizeof(vki_uid_t) ); POST_MEM_WRITE( ARG3, sizeof(vki_uid_t) ); } } PRE(sys_setresgid16) { PRINT("sys_setresgid16 ( %d, %d, %d )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "setresgid16", vki_old_gid_t, rgid, vki_old_gid_t, egid, vki_old_gid_t, sgid); } PRE(sys_setresgid) { PRINT("sys_setresgid ( %d, %d, %d )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "setresgid", vki_gid_t, rgid, vki_gid_t, egid, vki_gid_t, sgid); } PRE(sys_getresgid16) { PRINT("sys_getresgid16 ( %p, %p, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "getresgid16", vki_old_gid_t *, rgid, vki_old_gid_t *, egid, vki_old_gid_t *, sgid); PRE_MEM_WRITE( "getresgid16(rgid)", ARG1, sizeof(vki_old_gid_t) ); PRE_MEM_WRITE( "getresgid16(egid)", ARG2, sizeof(vki_old_gid_t) ); PRE_MEM_WRITE( "getresgid16(sgid)", ARG3, sizeof(vki_old_gid_t) ); } POST(sys_getresgid16) { vg_assert(SUCCESS); if (RES == 0) { POST_MEM_WRITE( ARG1, sizeof(vki_old_gid_t) ); POST_MEM_WRITE( ARG2, sizeof(vki_old_gid_t) ); POST_MEM_WRITE( ARG3, sizeof(vki_old_gid_t) ); } } PRE(sys_getresgid) { PRINT("sys_getresgid ( %p, %p, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "getresgid", vki_gid_t *, rgid, vki_gid_t *, egid, vki_gid_t *, sgid); PRE_MEM_WRITE( "getresgid(rgid)", ARG1, sizeof(vki_gid_t) ); PRE_MEM_WRITE( "getresgid(egid)", ARG2, sizeof(vki_gid_t) ); PRE_MEM_WRITE( "getresgid(sgid)", ARG3, sizeof(vki_gid_t) ); } POST(sys_getresgid) { vg_assert(SUCCESS); if (RES == 0) { POST_MEM_WRITE( ARG1, sizeof(vki_gid_t) ); POST_MEM_WRITE( ARG2, sizeof(vki_gid_t) ); POST_MEM_WRITE( ARG3, sizeof(vki_gid_t) ); } } /* --------------------------------------------------------------------- miscellaneous wrappers ------------------------------------------------------------------ */ PRE(sys_exit_group) { ThreadId t; ThreadState* tst; PRINT("exit_group( %d )", ARG1); PRE_REG_READ1(void, "exit_group", int, exit_code); tst = VG_(get_ThreadState)(tid); /* A little complex; find all the threads with the same threadgroup as this one (including this one), and mark them to exit */ for (t = 1; t < VG_N_THREADS; t++) { if ( /* not alive */ VG_(threads)[t].status == VgTs_Empty || /* not our group */ VG_(threads)[t].os_state.threadgroup != tst->os_state.threadgroup ) continue; VG_(threads)[t].exitreason = VgSrc_ExitSyscall; VG_(threads)[t].os_state.exitcode = ARG1; if (t != tid) VG_(kill_thread)(t); /* unblock it, if blocked */ } /* We have to claim the syscall already succeeded. */ SET_STATUS_Success(0); } PRE(sys_llseek) { PRINT("sys_llseek ( %d, 0x%x, 0x%x, %p, %d )", ARG1,ARG2,ARG3,ARG4,ARG5); PRE_REG_READ5(long, "llseek", unsigned int, fd, unsigned long, offset_high, unsigned long, offset_low, vki_loff_t *, result, unsigned int, whence); PRE_MEM_WRITE( "llseek(result)", ARG4, sizeof(vki_loff_t)); } POST(sys_llseek) { vg_assert(SUCCESS); if (RES == 0) POST_MEM_WRITE( ARG4, sizeof(vki_loff_t) ); } //zz PRE(sys_adjtimex, 0) //zz { //zz struct vki_timex *tx = (struct vki_timex *)ARG1; //zz PRINT("sys_adjtimex ( %p )", ARG1); //zz PRE_REG_READ1(long, "adjtimex", struct timex *, buf); //zz PRE_MEM_READ( "adjtimex(timex->modes)", ARG1, sizeof(tx->modes)); //zz #if 0 //zz (avoiding warnings about multi-line comments) zz #define ADJX(bit,field) \ zz if (tx->modes & bit) \ zz PRE_MEM_READ( "adjtimex(timex->"#field")", \ zz (Addr)&tx->field, sizeof(tx->field)) #endif //zz ADJX(ADJ_FREQUENCY, freq); //zz ADJX(ADJ_MAXERROR, maxerror); //zz ADJX(ADJ_ESTERROR, esterror); //zz ADJX(ADJ_STATUS, status); //zz ADJX(ADJ_TIMECONST, constant); //zz ADJX(ADJ_TICK, tick); //zz #undef ADJX //zz //zz PRE_MEM_WRITE( "adjtimex(timex)", ARG1, sizeof(struct vki_timex)); //zz } //zz //zz POST(sys_adjtimex) //zz { //zz POST_MEM_WRITE( ARG1, sizeof(struct vki_timex) ); //zz } PRE(sys_ioperm) { PRINT("sys_ioperm ( %d, %d, %d )", ARG1, ARG2, ARG3 ); PRE_REG_READ3(long, "ioperm", unsigned long, from, unsigned long, num, int, turn_on); } PRE(sys_syslog) { *flags |= SfMayBlock; PRINT("sys_syslog (%d, %p, %d)", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "syslog", int, type, char *, bufp, int, len); switch (ARG1) { // The kernel uses magic numbers here, rather than named constants, // therefore so do we. case 2: case 3: case 4: PRE_MEM_WRITE( "syslog(bufp)", ARG2, ARG3); break; default: break; } } POST(sys_syslog) { switch (ARG1) { case 2: case 3: case 4: POST_MEM_WRITE( ARG2, ARG3 ); break; default: break; } } PRE(sys_vhangup) { PRINT("sys_vhangup ( )"); PRE_REG_READ0(long, "vhangup"); } PRE(sys_sysinfo) { PRINT("sys_sysinfo ( %p )",ARG1); PRE_REG_READ1(long, "sysinfo", struct sysinfo *, info); PRE_MEM_WRITE( "sysinfo(info)", ARG1, sizeof(struct vki_sysinfo) ); } POST(sys_sysinfo) { POST_MEM_WRITE( ARG1, sizeof(struct vki_sysinfo) ); } PRE(sys_personality) { PRINT("sys_personality ( %llu )", (ULong)ARG1); PRE_REG_READ1(long, "personality", vki_u_long, persona); } PRE(sys_sysctl) { struct __vki_sysctl_args *args; PRINT("sys_sysctl ( %p )", ARG1 ); args = (struct __vki_sysctl_args *)ARG1; PRE_REG_READ1(long, "sysctl", struct __sysctl_args *, args); PRE_MEM_WRITE( "sysctl(args)", ARG1, sizeof(struct __vki_sysctl_args) ); if (!VG_(is_addressable)(ARG1, sizeof(struct __vki_sysctl_args), VKI_PROT_READ)) { SET_STATUS_Failure( VKI_EFAULT ); return; } PRE_MEM_READ("sysctl(name)", (Addr)args->name, args->nlen * sizeof(*args->name)); if (args->newval != NULL) PRE_MEM_READ("sysctl(newval)", (Addr)args->newval, args->newlen); if (args->oldlenp != NULL) { PRE_MEM_READ("sysctl(oldlenp)", (Addr)args->oldlenp, sizeof(*args->oldlenp)); PRE_MEM_WRITE("sysctl(oldval)", (Addr)args->oldval, *args->oldlenp); } } POST(sys_sysctl) { struct __vki_sysctl_args *args; args = (struct __vki_sysctl_args *)ARG1; if (args->oldlenp != NULL) { POST_MEM_WRITE((Addr)args->oldlenp, sizeof(*args->oldlenp)); POST_MEM_WRITE((Addr)args->oldval, 1 + *args->oldlenp); } } PRE(sys_prctl) { *flags |= SfMayBlock; PRINT( "prctl ( %d, %d, %d, %d, %d )", ARG1, ARG2, ARG3, ARG4, ARG5 ); // XXX: too simplistic, often not all args are used // Nb: can't use "ARG2".."ARG5" here because that's our own macro... PRE_REG_READ5(long, "prctl", int, option, unsigned long, arg2, unsigned long, arg3, unsigned long, arg4, unsigned long, arg5); // XXX: totally wrong... we need to look at the 'option' arg, and do // PRE_MEM_READs/PRE_MEM_WRITEs as necessary... } PRE(sys_sendfile) { *flags |= SfMayBlock; PRINT("sys_sendfile ( %d, %d, %p, %lu )", ARG1,ARG2,ARG3,ARG4); PRE_REG_READ4(ssize_t, "sendfile", int, out_fd, int, in_fd, vki_off_t *, offset, vki_size_t, count); if (ARG3 != 0) PRE_MEM_WRITE( "sendfile(offset)", ARG3, sizeof(vki_off_t) ); } POST(sys_sendfile) { if (ARG3 != 0 ) { POST_MEM_WRITE( ARG3, sizeof( vki_off_t ) ); } } PRE(sys_sendfile64) { *flags |= SfMayBlock; PRINT("sendfile64 ( %d, %d, %p, %lu )",ARG1,ARG2,ARG3,ARG4); PRE_REG_READ4(ssize_t, "sendfile64", int, out_fd, int, in_fd, vki_loff_t *, offset, vki_size_t, count); if (ARG3 != 0) PRE_MEM_WRITE( "sendfile64(offset)", ARG3, sizeof(vki_loff_t) ); } POST(sys_sendfile64) { if (ARG3 != 0 ) { POST_MEM_WRITE( ARG3, sizeof(vki_loff_t) ); } } PRE(sys_futex) { /* arg param used by ops ARG1 - u32 *futex all ARG2 - int op ARG3 - int val WAIT,WAKE,FD,REQUEUE,CMP_REQUEUE ARG4 - struct timespec *utime WAIT:time* REQUEUE,CMP_REQUEUE:val2 ARG5 - u32 *uaddr2 REQUEUE,CMP_REQUEUE ARG6 - int val3 CMP_REQUEUE */ PRINT("sys_futex ( %p, %d, %d, %p, %p )", ARG1,ARG2,ARG3,ARG4,ARG5); PRE_REG_READ6(long, "futex", vki_u32 *, futex, int, op, int, val, struct timespec *, utime, vki_u32 *, uaddr2, int, val3); PRE_MEM_READ( "futex(futex)", ARG1, sizeof(Int) ); *flags |= SfMayBlock; switch(ARG2) { case VKI_FUTEX_WAIT: if (ARG4 != 0) PRE_MEM_READ( "futex(timeout)", ARG4, sizeof(struct vki_timespec) ); break; case VKI_FUTEX_REQUEUE: case VKI_FUTEX_CMP_REQUEUE: PRE_MEM_READ( "futex(futex2)", ARG5, sizeof(Int) ); break; case VKI_FUTEX_WAKE: case VKI_FUTEX_FD: /* no additional pointers */ break; default: SET_STATUS_Failure( VKI_ENOSYS ); // some futex function we don't understand break; } } POST(sys_futex) { vg_assert(SUCCESS); POST_MEM_WRITE( ARG1, sizeof(int) ); if (ARG2 == VKI_FUTEX_FD) { if (!ML_(fd_allowed)(RES, "futex", tid, True)) { VG_(close)(RES); SET_STATUS_Failure( VKI_EMFILE ); } else { if (VG_(clo_track_fds)) ML_(record_fd_open_nameless)(tid, RES); } } } PRE(sys_mmap2) { // Exactly like old_mmap() in x86-linux except: // - all 6 args are passed in regs, rather than in a memory-block. // - the file offset is specified in pagesize units rather than bytes, // so that it can be used for files bigger than 2^32 bytes. PRINT("sys_mmap2 ( %p, %llu, %d, %d, %d, %d )", ARG1, (ULong)ARG2, ARG3, ARG4, ARG5, ARG6 ); PRE_REG_READ6(long, "mmap2", unsigned long, start, unsigned long, length, unsigned long, prot, unsigned long, flags, unsigned long, fd, unsigned long, offset); if (ARG2 == 0) { /* SuSV3 says: If len is zero, mmap() shall fail and no mapping shall be established. */ SET_STATUS_Failure( VKI_EINVAL ); return; } if (/*(ARG4 & VKI_MAP_FIXED) && */ (0 != (ARG1 & (VKI_PAGE_SIZE-1)))) { /* zap any misaligned addresses. */ /* SuSV3 says misaligned addresses only cause the MAP_FIXED case to fail. Here, we catch them all. */ SET_STATUS_Failure( VKI_EINVAL ); return; } if (ARG4 & VKI_MAP_FIXED) { if (!ML_(valid_client_addr)(ARG1, ARG2, tid, "mmap2")) SET_STATUS_Failure( VKI_ENOMEM ); } else { Addr a = VG_(find_map_space)(ARG1, ARG2, True); if (a == 0 && ARG1 != 0) a = VG_(find_map_space)(0, ARG2, True); if (a == 0) { SET_STATUS_Failure( VKI_ENOMEM ); } else { ARG1 = a; ARG4 |= VKI_MAP_FIXED; } } } POST(sys_mmap2) { vg_assert(SUCCESS); vg_assert(ML_(valid_client_addr)(RES, ARG2, tid, "mmap2")); ML_(mmap_segment)( (Addr)RES, ARG2, ARG3, ARG4, ARG5, ARG6 * (ULong)VKI_PAGE_SIZE ); } /* --------------------------------------------------------------------- epoll_* wrappers ------------------------------------------------------------------ */ PRE(sys_epoll_create) { PRINT("sys_epoll_create ( %d )", ARG1); PRE_REG_READ1(long, "epoll_create", int, size); } POST(sys_epoll_create) { vg_assert(SUCCESS); if (!ML_(fd_allowed)(RES, "epoll_create", tid, True)) { VG_(close)(RES); SET_STATUS_Failure( VKI_EMFILE ); } else { if (VG_(clo_track_fds)) ML_(record_fd_open_nameless) (tid, RES); } } PRE(sys_epoll_ctl) { static const HChar* epoll_ctl_s[3] = { "EPOLL_CTL_ADD", "EPOLL_CTL_DEL", "EPOLL_CTL_MOD" }; PRINT("sys_epoll_ctl ( %d, %s, %d, %p )", ARG1, ( ARG2<3 ? epoll_ctl_s[ARG2] : "?" ), ARG3, ARG4); PRE_REG_READ4(long, "epoll_ctl", int, epfd, int, op, int, fd, struct vki_epoll_event *, event); if (ARG2 != VKI_EPOLL_CTL_DEL) PRE_MEM_READ( "epoll_ctl(event)", ARG4, sizeof(struct vki_epoll_event) ); } PRE(sys_epoll_wait) { *flags |= SfMayBlock; PRINT("sys_epoll_wait ( %d, %p, %d, %d )", ARG1, ARG2, ARG3, ARG4); PRE_REG_READ4(long, "epoll_wait", int, epfd, struct vki_epoll_event *, events, int, maxevents, int, timeout); PRE_MEM_WRITE( "epoll_wait(events)", ARG2, sizeof(struct vki_epoll_event)*ARG3); } POST(sys_epoll_wait) { vg_assert(SUCCESS); if (RES > 0) POST_MEM_WRITE( ARG2, sizeof(struct vki_epoll_event)*RES ) ; } /* --------------------------------------------------------------------- tid-related wrappers ------------------------------------------------------------------ */ PRE(sys_gettid) { PRINT("sys_gettid ()"); PRE_REG_READ0(long, "gettid"); } PRE(sys_set_tid_address) { PRINT("sys_set_tid_address ( %p )", ARG1); PRE_REG_READ1(long, "set_tid_address", int *, tidptr); } //zz PRE(sys_tkill, Special) //zz { //zz /* int tkill(pid_t tid, int sig); */ //zz PRINT("sys_tkill ( %d, %d )", ARG1,ARG2); //zz PRE_REG_READ2(long, "tkill", int, tid, int, sig); //zz if (!ML_(client_signal_OK)(ARG2)) { //zz SET_STATUS_( -VKI_EINVAL ); //zz return; //zz } //zz //zz /* If we're sending SIGKILL, check to see if the target is one of //zz our threads and handle it specially. */ //zz if (ARG2 == VKI_SIGKILL && ML_(do_sigkill)(ARG1, -1)) //zz SET_STATUS_(0); //zz else //zz SET_STATUS_(VG_(do_syscall2)(SYSNO, ARG1, ARG2)); //zz //zz if (VG_(clo_trace_signals)) //zz VG_(message)(Vg_DebugMsg, "tkill: sent signal %d to pid %d", //zz ARG2, ARG1); //zz // Check to see if this kill gave us a pending signal //zz XXX FIXME VG_(poll_signals)(tid); //zz } PRE(sys_tgkill) { PRINT("sys_tgkill ( %d, %d, %d )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "tgkill", int, tgid, int, tid, int, sig); if (!ML_(client_signal_OK)(ARG3)) { SET_STATUS_Failure( VKI_EINVAL ); return; } /* If we're sending SIGKILL, check to see if the target is one of our threads and handle it specially. */ if (ARG3 == VKI_SIGKILL && ML_(do_sigkill)(ARG2, ARG1)) SET_STATUS_Success(0); else SET_STATUS_from_SysRes(VG_(do_syscall3)(SYSNO, ARG1, ARG2, ARG3)); if (VG_(clo_trace_signals)) VG_(message)(Vg_DebugMsg, "tgkill: sent signal %d to pid %d/%d", ARG3, ARG1, ARG2); /* Check to see if this kill gave us a pending signal */ *flags |= SfPollAfter; } POST(sys_tgkill) { if (VG_(clo_trace_signals)) VG_(message)(Vg_DebugMsg, "tgkill: sent signal %d to pid %d/%d", ARG3, ARG1, ARG2); } /* --------------------------------------------------------------------- fadvise64* wrappers ------------------------------------------------------------------ */ PRE(sys_fadvise64) { PRINT("sys_fadvise64 ( %d, %lld, %lu, %d )", ARG1, LOHI64(ARG2,ARG3), ARG4, ARG5); PRE_REG_READ5(long, "fadvise64", int, fd, vki_u32, offset_low, vki_u32, offset_high, vki_size_t, len, int, advice); } PRE(sys_fadvise64_64) { PRINT("sys_fadvise64_64 ( %d, %lld, %lld, %d )", ARG1, LOHI64(ARG2,ARG3), LOHI64(ARG4,ARG5), ARG6); PRE_REG_READ6(long, "fadvise64_64", int, fd, vki_u32, offset_low, vki_u32, offset_high, vki_u32, len_low, vki_u32, len_high, int, advice); } /* --------------------------------------------------------------------- io_* wrappers ------------------------------------------------------------------ */ // Nb: this wrapper has to pad/unpad memory around the syscall itself, // and this allows us to control exactly the code that gets run while // the padding is in place. PRE(sys_io_setup) { SizeT size; Addr addr; PRINT("sys_io_setup ( %u, %p )", ARG1,ARG2); PRE_REG_READ2(long, "io_setup", unsigned, nr_events, vki_aio_context_t *, ctxp); PRE_MEM_WRITE( "io_setup(ctxp)", ARG2, sizeof(vki_aio_context_t) ); size = VG_PGROUNDUP(sizeof(struct vki_aio_ring) + ARG1*sizeof(struct vki_io_event)); addr = VG_(find_map_space)(0, size, True); if (addr == 0) { SET_STATUS_Failure( VKI_ENOMEM ); return; } VG_(map_segment)(addr, size, VKI_PROT_READ|VKI_PROT_WRITE, 0); VG_(pad_address_space)(0); SET_STATUS_from_SysRes( VG_(do_syscall2)(SYSNO, ARG1, ARG2) ); VG_(unpad_address_space)(0); if (SUCCESS && RES == 0) { struct vki_aio_ring *r = *(struct vki_aio_ring **)ARG2; vg_assert(addr == (Addr)r); vg_assert(ML_(valid_client_addr)(addr, size, tid, "io_setup")); VG_TRACK( new_mem_mmap, addr, size, True, True, False ); POST_MEM_WRITE( ARG2, sizeof(vki_aio_context_t) ); } else { VG_(unmap_range)(addr, size); } } // Nb: This wrapper is "Special" because we need 'size' to do the unmap // after the syscall. We must get 'size' from the aio_ring structure, // before the syscall, while the aio_ring structure still exists. (And we // know that we must look at the aio_ring structure because Tom inspected the // kernel and glibc sources to see what they do, yuk.) // // XXX This segment can be implicitly unmapped when aio // file-descriptors are closed... PRE(sys_io_destroy) { Segment *s = VG_(find_segment)(ARG1); struct vki_aio_ring *r; SizeT size; PRINT("sys_io_destroy ( %llu )", (ULong)ARG1); PRE_REG_READ1(long, "io_destroy", vki_aio_context_t, ctx); // If we are going to seg fault (due to a bogus ARG1) do it as late as // possible... r = *(struct vki_aio_ring **)ARG1; size = VG_PGROUNDUP(sizeof(struct vki_aio_ring) + r->nr*sizeof(struct vki_io_event)); SET_STATUS_from_SysRes( VG_(do_syscall1)(SYSNO, ARG1) ); if (SUCCESS && RES == 0 && s != NULL) { VG_TRACK( die_mem_munmap, ARG1, size ); VG_(unmap_range)(ARG1, size); } } PRE(sys_io_getevents) { *flags |= SfMayBlock; PRINT("sys_io_getevents ( %llu, %lld, %lld, %p, %p )", (ULong)ARG1,(Long)ARG2,(Long)ARG3,ARG4,ARG5); PRE_REG_READ5(long, "io_getevents", vki_aio_context_t, ctx_id, long, min_nr, long, nr, struct io_event *, events, struct timespec *, timeout); if (ARG3 > 0) PRE_MEM_WRITE( "io_getevents(events)", ARG4, sizeof(struct vki_io_event)*ARG3 ); if (ARG5 != 0) PRE_MEM_READ( "io_getevents(timeout)", ARG5, sizeof(struct vki_timespec)); } POST(sys_io_getevents) { Int i; vg_assert(SUCCESS); if (RES > 0) { POST_MEM_WRITE( ARG4, sizeof(struct vki_io_event)*RES ); for (i = 0; i < RES; i++) { const struct vki_io_event *vev = ((struct vki_io_event *)ARG4) + i; const struct vki_iocb *cb = (struct vki_iocb *)(Addr)vev->obj; switch (cb->aio_lio_opcode) { case VKI_IOCB_CMD_PREAD: if (vev->result > 0) POST_MEM_WRITE( cb->aio_buf, vev->result ); break; case VKI_IOCB_CMD_PWRITE: break; default: VG_(message)(Vg_DebugMsg, "Warning: unhandled io_getevents opcode: %u\n", cb->aio_lio_opcode); break; } } } } PRE(sys_io_submit) { Int i; PRINT("sys_io_submit ( %llu, %ld, %p )", (ULong)ARG1,ARG2,ARG3); PRE_REG_READ3(long, "io_submit", vki_aio_context_t, ctx_id, long, nr, struct iocb **, iocbpp); PRE_MEM_READ( "io_submit(iocbpp)", ARG3, ARG2*sizeof(struct vki_iocb *) ); if (ARG3 != 0) { for (i = 0; i < ARG2; i++) { struct vki_iocb *cb = ((struct vki_iocb **)ARG3)[i]; PRE_MEM_READ( "io_submit(iocb)", (Addr)cb, sizeof(struct vki_iocb) ); switch (cb->aio_lio_opcode) { case VKI_IOCB_CMD_PREAD: PRE_MEM_WRITE( "io_submit(PREAD)", cb->aio_buf, cb->aio_nbytes ); break; case VKI_IOCB_CMD_PWRITE: PRE_MEM_READ( "io_submit(PWRITE)", cb->aio_buf, cb->aio_nbytes ); break; default: VG_(message)(Vg_DebugMsg,"Warning: unhandled io_submit opcode: %u\n", cb->aio_lio_opcode); break; } } } } PRE(sys_io_cancel) { PRINT("sys_io_cancel ( %llu, %p, %p )", (ULong)ARG1,ARG2,ARG3); PRE_REG_READ3(long, "io_cancel", vki_aio_context_t, ctx_id, struct iocb *, iocb, struct io_event *, result); PRE_MEM_READ( "io_cancel(iocb)", ARG2, sizeof(struct vki_iocb) ); PRE_MEM_WRITE( "io_cancel(result)", ARG3, sizeof(struct vki_io_event) ); } POST(sys_io_cancel) { POST_MEM_WRITE( ARG3, sizeof(struct vki_io_event) ); } /* --------------------------------------------------------------------- *_mempolicy wrappers ------------------------------------------------------------------ */ PRE(sys_set_mempolicy) { PRINT("sys_set_mempolicy ( %d, %p, %d )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "set_mempolicy", int, policy, unsigned long *, nodemask, unsigned long, maxnode); PRE_MEM_READ( "set_mempolicy(nodemask)", ARG2, VG_ROUNDUP( ARG3, sizeof(UWord) ) / sizeof(UWord) ); } PRE(sys_get_mempolicy) { PRINT("sys_get_mempolicy ( %p, %p, %d, %p, %x )", ARG1,ARG2,ARG3,ARG4,ARG5); PRE_REG_READ5(long, "get_mempolicy", int *, policy, unsigned long *, nodemask, unsigned long, maxnode, unsigned long, addr, unsigned long, flags); if (ARG1 != 0) PRE_MEM_WRITE( "get_mempolicy(policy)", ARG1, sizeof(Int) ); if (ARG2 != 0) PRE_MEM_WRITE( "get_mempolicy(nodemask)", ARG2, VG_ROUNDUP( ARG3, sizeof(UWord) * 8 ) / sizeof(UWord) ); } POST(sys_get_mempolicy) { if (ARG1 != 0) POST_MEM_WRITE( ARG1, sizeof(Int) ); if (ARG2 != 0) POST_MEM_WRITE( ARG2, VG_ROUNDUP( ARG3, sizeof(UWord) * 8 ) / sizeof(UWord) ); } /* --------------------------------------------------------------------- inotify_* wrappers ------------------------------------------------------------------ */ PRE(sys_inotify_init) { PRINT("sys_inotify_init ( )"); PRE_REG_READ0(long, "inotify_init"); } POST(sys_inotify_init) { vg_assert(SUCCESS); if (!ML_(fd_allowed)(RES, "inotify_init", tid, True)) { VG_(close)(RES); SET_STATUS_Failure( VKI_EMFILE ); } else { if (VG_(clo_track_fds)) ML_(record_fd_open_nameless) (tid, RES); } } PRE(sys_inotify_add_watch) { PRINT( "sys_inotify_add_watch ( %d, %p, %x )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "inotify_add_watch", int, fd, char *, path, int, mask); PRE_MEM_RASCIIZ( "inotify_add_watch(path)", ARG2 ); } PRE(sys_inotify_rm_watch) { PRINT( "sys_inotify_rm_watch ( %d, %x )", ARG1,ARG2); PRE_REG_READ2(long, "inotify_rm_watch", int, fd, int, wd); } /* --------------------------------------------------------------------- mq_* wrappers ------------------------------------------------------------------ */ PRE(sys_mq_open) { PRINT("sys_mq_open( %p(%s), %d, %lld, %p )", ARG1,ARG1,ARG2,(ULong)ARG3,ARG4); PRE_REG_READ4(long, "mq_open", const char *, name, int, oflag, vki_mode_t, mode, struct mq_attr *, attr); PRE_MEM_RASCIIZ( "mq_open(name)", ARG1 ); if ((ARG2 & VKI_O_CREAT) != 0 && ARG4 != 0) { const struct vki_mq_attr *attr = (struct vki_mq_attr *)ARG4; PRE_MEM_READ( "mq_open(attr->mq_maxmsg)", (Addr)&attr->mq_maxmsg, sizeof(attr->mq_maxmsg) ); PRE_MEM_READ( "mq_open(attr->mq_msgsize)", (Addr)&attr->mq_msgsize, sizeof(attr->mq_msgsize) ); } } POST(sys_mq_open) { vg_assert(SUCCESS); if (!ML_(fd_allowed)(RES, "mq_open", tid, True)) { VG_(close)(RES); SET_STATUS_Failure( VKI_EMFILE ); } else { if (VG_(clo_track_fds)) ML_(record_fd_open_with_given_name)(tid, RES, (Char*)ARG1); } } PRE(sys_mq_unlink) { PRINT("sys_mq_unlink ( %p(%s) )", ARG1,ARG1); PRE_REG_READ1(long, "mq_unlink", const char *, name); PRE_MEM_RASCIIZ( "mq_unlink(name)", ARG1 ); } PRE(sys_mq_timedsend) { *flags |= SfMayBlock; PRINT("sys_mq_timedsend ( %d, %p, %llu, %d, %p )", ARG1,ARG2,(ULong)ARG3,ARG4,ARG5); PRE_REG_READ5(long, "mq_timedsend", vki_mqd_t, mqdes, const char *, msg_ptr, vki_size_t, msg_len, unsigned int, msg_prio, const struct timespec *, abs_timeout); if (!ML_(fd_allowed)(ARG1, "mq_timedsend", tid, False)) { SET_STATUS_Failure( VKI_EBADF ); } else { PRE_MEM_READ( "mq_timedsend(msg_ptr)", ARG2, ARG3 ); if (ARG5 != 0) PRE_MEM_READ( "mq_timedsend(abs_timeout)", ARG5, sizeof(struct vki_timespec) ); } } PRE(sys_mq_timedreceive) { *flags |= SfMayBlock; PRINT("sys_mq_timedreceive( %d, %p, %llu, %p, %p )", ARG1,ARG2,(ULong)ARG3,ARG4,ARG5); PRE_REG_READ5(ssize_t, "mq_timedreceive", vki_mqd_t, mqdes, char *, msg_ptr, vki_size_t, msg_len, unsigned int *, msg_prio, const struct timespec *, abs_timeout); if (!ML_(fd_allowed)(ARG1, "mq_timedreceive", tid, False)) { SET_STATUS_Failure( VKI_EBADF ); } else { PRE_MEM_WRITE( "mq_timedreceive(msg_ptr)", ARG2, ARG3 ); if (ARG4 != 0) PRE_MEM_WRITE( "mq_timedreceive(msg_prio)", ARG4, sizeof(unsigned int) ); if (ARG5 != 0) PRE_MEM_READ( "mq_timedreceive(abs_timeout)", ARG5, sizeof(struct vki_timespec) ); } } POST(sys_mq_timedreceive) { POST_MEM_WRITE( ARG2, ARG3 ); if (ARG4 != 0) POST_MEM_WRITE( ARG4, sizeof(unsigned int) ); } PRE(sys_mq_notify) { PRINT("sys_mq_notify( %d, %p )", ARG1,ARG2 ); PRE_REG_READ2(long, "mq_notify", vki_mqd_t, mqdes, const struct sigevent *, notification); if (!ML_(fd_allowed)(ARG1, "mq_notify", tid, False)) SET_STATUS_Failure( VKI_EBADF ); else if (ARG2 != 0) PRE_MEM_READ( "mq_notify(notification)", ARG2, sizeof(struct vki_sigevent) ); } PRE(sys_mq_getsetattr) { PRINT("sys_mq_getsetattr( %d, %p, %p )", ARG1,ARG2,ARG3 ); PRE_REG_READ3(long, "mq_getsetattr", vki_mqd_t, mqdes, const struct mq_attr *, mqstat, struct mq_attr *, omqstat); if (!ML_(fd_allowed)(ARG1, "mq_getsetattr", tid, False)) { SET_STATUS_Failure( VKI_EBADF ); } else { if (ARG2 != 0) { const struct vki_mq_attr *attr = (struct vki_mq_attr *)ARG2; PRE_MEM_READ( "mq_getsetattr(mqstat->mq_flags)", (Addr)&attr->mq_flags, sizeof(attr->mq_flags) ); } if (ARG3 != 0) PRE_MEM_WRITE( "mq_getsetattr(omqstat)", ARG3, sizeof(struct vki_mq_attr) ); } } POST(sys_mq_getsetattr) { if (ARG3 != 0) POST_MEM_WRITE( ARG3, sizeof(struct vki_mq_attr) ); } /* --------------------------------------------------------------------- clock_* wrappers ------------------------------------------------------------------ */ PRE(sys_clock_settime) { PRINT("sys_clock_settime( %d, %p )", ARG1,ARG2); PRE_REG_READ2(long, "clock_settime", vki_clockid_t, clk_id, const struct timespec *, tp); PRE_MEM_READ( "clock_settime(tp)", ARG2, sizeof(struct vki_timespec) ); } PRE(sys_clock_gettime) { PRINT("sys_clock_gettime( %d, %p )" , ARG1,ARG2); PRE_REG_READ2(long, "clock_gettime", vki_clockid_t, clk_id, struct timespec *, tp); PRE_MEM_WRITE( "clock_gettime(tp)", ARG2, sizeof(struct vki_timespec) ); } POST(sys_clock_gettime) { POST_MEM_WRITE( ARG2, sizeof(struct vki_timespec) ); } PRE(sys_clock_getres) { PRINT("sys_clock_getres( %d, %p )" , ARG1,ARG2); // Nb: we can't use "RES" as the param name because that's a macro // defined above! PRE_REG_READ2(long, "clock_getres", vki_clockid_t, clk_id, struct timespec *, res); if (ARG2 != 0) PRE_MEM_WRITE( "clock_getres(res)", ARG2, sizeof(struct vki_timespec) ); } POST(sys_clock_getres) { if (ARG2 != 0) POST_MEM_WRITE( ARG2, sizeof(struct vki_timespec) ); } PRE(sys_clock_nanosleep) { *flags |= SfMayBlock|SfPostOnFail; PRINT("sys_clock_nanosleep( %d, %d, %p, %p )", ARG1,ARG2,ARG3,ARG4); PRE_REG_READ4(int32_t, "clock_nanosleep", vki_clockid_t, clkid, int, flags, const struct timespec *, rqtp, struct timespec *, rmtp); PRE_MEM_READ( "clock_nanosleep(rqtp)", ARG3, sizeof(struct vki_timespec) ); if (ARG4 != 0) PRE_MEM_WRITE( "clock_nanosleep(rmtp)", ARG4, sizeof(struct vki_timespec) ); } POST(sys_clock_nanosleep) { if (ARG4 != 0 && FAILURE && RES_unchecked == VKI_EINTR) POST_MEM_WRITE( ARG4, sizeof(struct vki_timespec) ); } /* --------------------------------------------------------------------- timer_* wrappers ------------------------------------------------------------------ */ PRE(sys_timer_create) { PRINT("sys_timer_create( %d, %p, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "timer_create", vki_clockid_t, clockid, struct sigevent *, evp, vki_timer_t *, timerid); if (ARG2 != 0) PRE_MEM_READ( "timer_create(evp)", ARG2, sizeof(struct vki_sigevent) ); PRE_MEM_WRITE( "timer_create(timerid)", ARG3, sizeof(vki_timer_t) ); } POST(sys_timer_create) { POST_MEM_WRITE( ARG3, sizeof(vki_timer_t) ); } PRE(sys_timer_settime) { PRINT("sys_timer_settime( %lld, %d, %p, %p )", (ULong)ARG1,ARG2,ARG3,ARG4); PRE_REG_READ4(long, "timer_settime", vki_timer_t, timerid, int, flags, const struct itimerspec *, value, struct itimerspec *, ovalue); PRE_MEM_READ( "timer_settime(value)", ARG3, sizeof(struct vki_itimerspec) ); if (ARG4 != 0) PRE_MEM_WRITE( "timer_settime(ovalue)", ARG4, sizeof(struct vki_itimerspec) ); } POST(sys_timer_settime) { if (ARG4 != 0) POST_MEM_WRITE( ARG4, sizeof(struct vki_itimerspec) ); } PRE(sys_timer_gettime) { PRINT("sys_timer_gettime( %lld, %p )", (ULong)ARG1,ARG2); PRE_REG_READ2(long, "timer_gettime", vki_timer_t, timerid, struct itimerspec *, value); PRE_MEM_WRITE( "timer_gettime(value)", ARG2, sizeof(struct vki_itimerspec)); } POST(sys_timer_gettime) { POST_MEM_WRITE( ARG2, sizeof(struct vki_itimerspec) ); } PRE(sys_timer_getoverrun) { PRINT("sys_timer_getoverrun( %p )", ARG1); PRE_REG_READ1(long, "timer_getoverrun", vki_timer_t, timerid); } PRE(sys_timer_delete) { PRINT("sys_timer_delete( %p )", ARG1); PRE_REG_READ1(long, "timer_delete", vki_timer_t, timerid); } /* --------------------------------------------------------------------- capabilities wrappers ------------------------------------------------------------------ */ PRE(sys_capget) { PRINT("sys_capget ( %p, %p )", ARG1, ARG2 ); PRE_REG_READ2(long, "capget", vki_cap_user_header_t, header, vki_cap_user_data_t, data); PRE_MEM_READ( "capget(header)", ARG1, sizeof(struct __vki_user_cap_header_struct) ); PRE_MEM_WRITE( "capget(data)", ARG2, sizeof(struct __vki_user_cap_data_struct) ); } POST(sys_capget) { if (ARG2 != (Addr)NULL) POST_MEM_WRITE( ARG2, sizeof(struct __vki_user_cap_data_struct) ); } PRE(sys_capset) { PRINT("sys_capset ( %p, %p )", ARG1, ARG2 ); PRE_REG_READ2(long, "capset", vki_cap_user_header_t, header, const vki_cap_user_data_t, data); PRE_MEM_READ( "capset(header)", ARG1, sizeof(struct __vki_user_cap_header_struct) ); PRE_MEM_READ( "capset(data)", ARG2, sizeof(struct __vki_user_cap_data_struct) ); } /* --------------------------------------------------------------------- 16-bit uid/gid/groups wrappers ------------------------------------------------------------------ */ PRE(sys_getuid16) { PRINT("sys_getuid16 ( )"); PRE_REG_READ0(long, "getuid16"); } PRE(sys_setuid16) { PRINT("sys_setuid16 ( %d )", ARG1); PRE_REG_READ1(long, "setuid16", vki_old_uid_t, uid); } PRE(sys_getgid16) { PRINT("sys_getgid16 ( )"); PRE_REG_READ0(long, "getgid16"); } PRE(sys_setgid16) { PRINT("sys_setgid16 ( %d )", ARG1); PRE_REG_READ1(long, "setgid16", vki_old_gid_t, gid); } PRE(sys_geteuid16) { PRINT("sys_geteuid16 ( )"); PRE_REG_READ0(long, "geteuid16"); } PRE(sys_getegid16) { PRINT("sys_getegid16 ( )"); PRE_REG_READ0(long, "getegid16"); } PRE(sys_setreuid16) { PRINT("setreuid16 ( 0x%x, 0x%x )", ARG1, ARG2); PRE_REG_READ2(long, "setreuid16", vki_old_uid_t, ruid, vki_old_uid_t, euid); } PRE(sys_setregid16) { PRINT("sys_setregid16 ( %d, %d )", ARG1, ARG2); PRE_REG_READ2(long, "setregid16", vki_old_gid_t, rgid, vki_old_gid_t, egid); } PRE(sys_getgroups16) { PRINT("sys_getgroups16 ( %d, %p )", ARG1, ARG2); PRE_REG_READ2(long, "getgroups16", int, size, vki_old_gid_t *, list); if (ARG1 > 0) PRE_MEM_WRITE( "getgroups16(list)", ARG2, ARG1 * sizeof(vki_old_gid_t) ); } POST(sys_getgroups16) { vg_assert(SUCCESS); if (ARG1 > 0 && RES > 0) POST_MEM_WRITE( ARG2, RES * sizeof(vki_old_gid_t) ); } PRE(sys_setgroups16) { PRINT("sys_setgroups16 ( %llu, %p )", (ULong)ARG1, ARG2); PRE_REG_READ2(long, "setgroups16", int, size, vki_old_gid_t *, list); if (ARG1 > 0) PRE_MEM_READ( "setgroups16(list)", ARG2, ARG1 * sizeof(vki_old_gid_t) ); } /* --------------------------------------------------------------------- *chown16 wrappers ------------------------------------------------------------------ */ PRE(sys_chown16) { PRINT("sys_chown16 ( %p, 0x%x, 0x%x )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "chown16", const char *, path, vki_old_uid_t, owner, vki_old_gid_t, group); PRE_MEM_RASCIIZ( "chown16(path)", ARG1 ); } PRE(sys_fchown16) { PRINT("sys_fchown16 ( %d, %d, %d )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "fchown16", unsigned int, fd, vki_old_uid_t, owner, vki_old_gid_t, group); } /* --------------------------------------------------------------------- *xattr wrappers ------------------------------------------------------------------ */ PRE(sys_setxattr) { *flags |= SfMayBlock; PRINT("sys_setxattr ( %p, %p, %p, %llu, %d )", ARG1, ARG2, ARG3, (ULong)ARG4, ARG5); PRE_REG_READ5(long, "setxattr", char *, path, char *, name, void *, value, vki_size_t, size, int, flags); PRE_MEM_RASCIIZ( "setxattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "setxattr(name)", ARG2 ); PRE_MEM_READ( "setxattr(value)", ARG3, ARG4 ); } PRE(sys_lsetxattr) { *flags |= SfMayBlock; PRINT("sys_lsetxattr ( %p, %p, %p, %llu, %d )", ARG1, ARG2, ARG3, (ULong)ARG4, ARG5); PRE_REG_READ5(long, "lsetxattr", char *, path, char *, name, void *, value, vki_size_t, size, int, flags); PRE_MEM_RASCIIZ( "lsetxattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "lsetxattr(name)", ARG2 ); PRE_MEM_READ( "lsetxattr(value)", ARG3, ARG4 ); } PRE(sys_fsetxattr) { *flags |= SfMayBlock; PRINT("sys_fsetxattr ( %d, %p, %p, %llu, %d )", ARG1, ARG2, ARG3, (ULong)ARG4, ARG5); PRE_REG_READ5(long, "fsetxattr", int, fd, char *, name, void *, value, vki_size_t, size, int, flags); PRE_MEM_RASCIIZ( "fsetxattr(name)", ARG2 ); PRE_MEM_READ( "fsetxattr(value)", ARG3, ARG4 ); } PRE(sys_getxattr) { *flags |= SfMayBlock; PRINT("sys_getxattr ( %p, %p, %p, %llu )", ARG1,ARG2,ARG3, (ULong)ARG4); PRE_REG_READ4(ssize_t, "getxattr", char *, path, char *, name, void *, value, vki_size_t, size); PRE_MEM_RASCIIZ( "getxattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "getxattr(name)", ARG2 ); PRE_MEM_WRITE( "getxattr(value)", ARG3, ARG4 ); } POST(sys_getxattr) { vg_assert(SUCCESS); if (RES > 0 && ARG3 != (Addr)NULL) { POST_MEM_WRITE( ARG3, RES ); } } PRE(sys_lgetxattr) { *flags |= SfMayBlock; PRINT("sys_lgetxattr ( %p, %p, %p, %llu )", ARG1,ARG2,ARG3, (ULong)ARG4); PRE_REG_READ4(ssize_t, "lgetxattr", char *, path, char *, name, void *, value, vki_size_t, size); PRE_MEM_RASCIIZ( "lgetxattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "lgetxattr(name)", ARG2 ); PRE_MEM_WRITE( "lgetxattr(value)", ARG3, ARG4 ); } POST(sys_lgetxattr) { vg_assert(SUCCESS); if (RES > 0 && ARG3 != (Addr)NULL) { POST_MEM_WRITE( ARG3, RES ); } } PRE(sys_fgetxattr) { *flags |= SfMayBlock; PRINT("sys_fgetxattr ( %d, %p, %p, %llu )", ARG1, ARG2, ARG3, (ULong)ARG4); PRE_REG_READ4(ssize_t, "fgetxattr", int, fd, char *, name, void *, value, vki_size_t, size); PRE_MEM_RASCIIZ( "fgetxattr(name)", ARG2 ); PRE_MEM_WRITE( "fgetxattr(value)", ARG3, ARG4 ); } POST(sys_fgetxattr) { if (RES > 0 && ARG3 != (Addr)NULL) POST_MEM_WRITE( ARG3, RES ); } PRE(sys_listxattr) { *flags |= SfMayBlock; PRINT("sys_listxattr ( %p, %p, %llu )", ARG1, ARG2, (ULong)ARG3); PRE_REG_READ3(ssize_t, "listxattr", char *, path, char *, list, vki_size_t, size); PRE_MEM_RASCIIZ( "listxattr(path)", ARG1 ); PRE_MEM_WRITE( "listxattr(list)", ARG2, ARG3 ); } POST(sys_listxattr) { if (RES > 0 && ARG2 != (Addr)NULL) POST_MEM_WRITE( ARG2, RES ); } PRE(sys_llistxattr) { *flags |= SfMayBlock; PRINT("sys_llistxattr ( %p, %p, %llu )", ARG1, ARG2, (ULong)ARG3); PRE_REG_READ3(ssize_t, "llistxattr", char *, path, char *, list, vki_size_t, size); PRE_MEM_RASCIIZ( "llistxattr(path)", ARG1 ); PRE_MEM_WRITE( "llistxattr(list)", ARG2, ARG3 ); } POST(sys_llistxattr) { if (RES > 0 && ARG2 != (Addr)NULL) POST_MEM_WRITE( ARG2, RES ); } PRE(sys_flistxattr) { *flags |= SfMayBlock; PRINT("sys_flistxattr ( %d, %p, %llu )", ARG1, ARG2, (ULong)ARG3); PRE_REG_READ3(ssize_t, "flistxattr", int, fd, char *, list, vki_size_t, size); PRE_MEM_WRITE( "flistxattr(list)", ARG2, ARG3 ); } POST(sys_flistxattr) { if (RES > 0 && ARG2 != (Addr)NULL) POST_MEM_WRITE( ARG2, RES ); } PRE(sys_removexattr) { *flags |= SfMayBlock; PRINT("sys_removexattr ( %p, %p )", ARG1, ARG2); PRE_REG_READ2(long, "removexattr", char *, path, char *, name); PRE_MEM_RASCIIZ( "removexattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "removexattr(name)", ARG2 ); } PRE(sys_lremovexattr) { *flags |= SfMayBlock; PRINT("sys_lremovexattr ( %p, %p )", ARG1, ARG2); PRE_REG_READ2(long, "lremovexattr", char *, path, char *, name); PRE_MEM_RASCIIZ( "lremovexattr(path)", ARG1 ); PRE_MEM_RASCIIZ( "lremovexattr(name)", ARG2 ); } PRE(sys_fremovexattr) { *flags |= SfMayBlock; PRINT("sys_fremovexattr ( %d, %p )", ARG1, ARG2); PRE_REG_READ2(long, "fremovexattr", int, fd, char *, name); PRE_MEM_RASCIIZ( "fremovexattr(name)", ARG2 ); } /* --------------------------------------------------------------------- sched_* wrappers ------------------------------------------------------------------ */ PRE(sys_sched_setparam) { PRINT("sched_setparam ( %d, %p )", ARG1, ARG2 ); PRE_REG_READ2(long, "sched_setparam", vki_pid_t, pid, struct sched_param *, p); PRE_MEM_READ( "sched_setparam(p)", ARG2, sizeof(struct vki_sched_param) ); } POST(sys_sched_setparam) { POST_MEM_WRITE( ARG2, sizeof(struct vki_sched_param) ); } PRE(sys_sched_getparam) { PRINT("sched_getparam ( %d, %p )", ARG1, ARG2 ); PRE_REG_READ2(long, "sched_getparam", vki_pid_t, pid, struct sched_param *, p); PRE_MEM_WRITE( "sched_getparam(p)", ARG2, sizeof(struct vki_sched_param) ); } POST(sys_sched_getparam) { POST_MEM_WRITE( ARG2, sizeof(struct vki_sched_param) ); } PRE(sys_sched_getscheduler) { PRINT("sys_sched_getscheduler ( %d )", ARG1); PRE_REG_READ1(long, "sched_getscheduler", vki_pid_t, pid); } PRE(sys_sched_setscheduler) { PRINT("sys_sched_setscheduler ( %d, %d, %p )", ARG1,ARG2,ARG3); PRE_REG_READ3(long, "sched_setscheduler", vki_pid_t, pid, int, policy, struct sched_param *, p); if (ARG3 != 0) PRE_MEM_READ( "sched_setscheduler(p)", ARG3, sizeof(struct vki_sched_param)); } PRE(sys_sched_yield) { *flags |= SfMayBlock; PRINT("sched_yield()"); PRE_REG_READ0(long, "sys_sched_yield"); } PRE(sys_sched_get_priority_max) { PRINT("sched_get_priority_max ( %d )", ARG1); PRE_REG_READ1(long, "sched_get_priority_max", int, policy); } PRE(sys_sched_get_priority_min) { PRINT("sched_get_priority_min ( %d )", ARG1); PRE_REG_READ1(long, "sched_get_priority_min", int, policy); } PRE(sys_sched_setaffinity) { PRINT("sched_setaffinity ( %d, %d, %p )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "sched_setaffinity", vki_pid_t, pid, unsigned int, len, unsigned long *, mask); PRE_MEM_READ( "sched_setaffinity(mask)", ARG3, ARG2); } PRE(sys_sched_getaffinity) { PRINT("sched_getaffinity ( %d, %d, %p )", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "sched_getaffinity", vki_pid_t, pid, unsigned int, len, unsigned long *, mask); PRE_MEM_WRITE( "sched_getaffinity(mask)", ARG3, ARG2); } POST(sys_sched_getaffinity) { POST_MEM_WRITE(ARG3, ARG2); } /* --------------------------------------------------------------------- miscellaneous wrappers ------------------------------------------------------------------ */ PRE(sys_munlockall) { *flags |= SfMayBlock; PRINT("sys_munlockall ( )"); PRE_REG_READ0(long, "munlockall"); } // This has different signatures for different platforms. // // x86: int sys_pipe(unsigned long __user *fildes); // AMD64: long sys_pipe(int *fildes); // ppc32: int sys_pipe(int __user *fildes); // ppc64: int sys_pipe(int __user *fildes); // // The type of the argument is most important, and it is an array of 32 bit // values in all cases. (The return type differs across platforms, but it // is not used.) So we use 'int' as its type. This fixed bug #113230 which // was caused by using an array of 'unsigned long's, which didn't work on // AMD64. PRE(sys_pipe) { PRINT("sys_pipe ( %p )", ARG1); PRE_REG_READ1(int, "pipe", int *, filedes); PRE_MEM_WRITE( "pipe(filedes)", ARG1, 2*sizeof(int) ); } POST(sys_pipe) { Int *p = (Int *)ARG1; if (!ML_(fd_allowed)(p[0], "pipe", tid, True) || !ML_(fd_allowed)(p[1], "pipe", tid, True)) { VG_(close)(p[0]); VG_(close)(p[1]); SET_STATUS_Failure( VKI_EMFILE ); } else { POST_MEM_WRITE( ARG1, 2*sizeof(int) ); if (VG_(clo_track_fds)) { ML_(record_fd_open_nameless)(tid, p[0]); ML_(record_fd_open_nameless)(tid, p[1]); } } } PRE(sys_quotactl) { PRINT("sys_quotactl (0x%x, %p, 0x%x, 0x%x )", ARG1,ARG2,ARG3, ARG4); PRE_REG_READ4(long, "quotactl", unsigned int, cmd, const char *, special, vki_qid_t, id, void *, addr); PRE_MEM_RASCIIZ( "quotactl(special)", ARG2 ); } PRE(sys_waitid) { *flags |= SfMayBlock; PRINT("sys_waitid( %d, %d, %p, %d, %p )", ARG1,ARG2,ARG3,ARG4,ARG5); PRE_REG_READ5(int32_t, "sys_waitid", int, which, vki_pid_t, pid, struct vki_siginfo *, infop, int, options, struct vki_rusage *, ru); PRE_MEM_WRITE( "waitid(infop)", ARG3, sizeof(struct vki_siginfo) ); if (ARG5 != 0) PRE_MEM_WRITE( "waitid(ru)", ARG5, sizeof(struct vki_rusage) ); } POST(sys_waitid) { POST_MEM_WRITE( ARG3, sizeof(struct vki_siginfo) ); if (ARG5 != 0) POST_MEM_WRITE( ARG5, sizeof(struct vki_rusage) ); } /* --------------------------------------------------------------------- utime wrapper ------------------------------------------------------------------ */ PRE(sys_utime) { *flags |= SfMayBlock; PRINT("sys_utime ( %p, %p )", ARG1,ARG2); PRE_REG_READ2(long, "utime", char *, filename, struct utimbuf *, buf); PRE_MEM_RASCIIZ( "utime(filename)", ARG1 ); if (ARG2 != 0) PRE_MEM_READ( "utime(buf)", ARG2, sizeof(struct vki_utimbuf) ); } /* --------------------------------------------------------------------- lseek wrapper ------------------------------------------------------------------ */ PRE(sys_lseek) { PRINT("sys_lseek ( %d, %d, %d )", ARG1,ARG2,ARG3); PRE_REG_READ3(vki_off_t, "lseek", unsigned int, fd, vki_off_t, offset, unsigned int, whence); } /* --------------------------------------------------------------------- sig* wrappers ------------------------------------------------------------------ */ PRE(sys_sigpending) { PRINT( "sys_sigpending ( %p )", ARG1 ); PRE_REG_READ1(long, "sigpending", vki_old_sigset_t *, set); PRE_MEM_WRITE( "sigpending(set)", ARG1, sizeof(vki_old_sigset_t)); } POST(sys_sigpending) { POST_MEM_WRITE( ARG1, sizeof(vki_old_sigset_t) ) ; } // This syscall is not used on amd64/Linux -- it only provides // sys_rt_sigprocmask, which uses sigset_t rather than old_sigset_t. // This wrapper is only suitable for 32-bit architectures. // (XXX: so how is it that PRE(sys_sigpending) above doesn't need // conditional compilation like this?) #if defined(VGP_x86_linux) || defined(VGP_ppc32_linux) PRE(sys_sigprocmask) { vki_old_sigset_t* set; vki_old_sigset_t* oldset; vki_sigset_t bigger_set; vki_sigset_t bigger_oldset; PRINT("sys_sigprocmask ( %d, %p, %p )",ARG1,ARG2,ARG3); PRE_REG_READ3(long, "sigprocmask", int, how, vki_old_sigset_t *, set, vki_old_sigset_t *, oldset); if (ARG2 != 0) PRE_MEM_READ( "sigprocmask(set)", ARG2, sizeof(vki_old_sigset_t)); if (ARG3 != 0) PRE_MEM_WRITE( "sigprocmask(oldset)", ARG3, sizeof(vki_old_sigset_t)); // Nb: We must convert the smaller vki_old_sigset_t params into bigger // vki_sigset_t params. set = (vki_old_sigset_t*)ARG2; oldset = (vki_old_sigset_t*)ARG3; VG_(memset)(&bigger_set, 0, sizeof(vki_sigset_t)); VG_(memset)(&bigger_oldset, 0, sizeof(vki_sigset_t)); if (set) bigger_set.sig[0] = *(vki_old_sigset_t*)set; SET_STATUS_from_SysRes( VG_(do_sys_sigprocmask) ( tid, ARG1 /*how*/, set ? &bigger_set : NULL, oldset ? &bigger_oldset : NULL) ); if (oldset) *oldset = bigger_oldset.sig[0]; if (SUCCESS) *flags |= SfPollAfter; } POST(sys_sigprocmask) { vg_assert(SUCCESS); if (RES == 0 && ARG3 != 0) POST_MEM_WRITE( ARG3, sizeof(vki_old_sigset_t)); } #endif /* --------------------------------------------------------------------- rt_sig* wrappers ------------------------------------------------------------------ */ PRE(sys_rt_sigaction) { PRINT("sys_rt_sigaction ( %d, %p, %p, %d )", ARG1,ARG2,ARG3,ARG4); PRE_REG_READ4(long, "rt_sigaction", int, signum, const struct sigaction *, act, struct sigaction *, oldact, vki_size_t, sigsetsize); if (ARG2 != 0) { struct vki_sigaction *sa = (struct vki_sigaction *)ARG2; PRE_MEM_READ( "rt_sigaction(act->sa_handler)", (Addr)&sa->ksa_handler, sizeof(sa->ksa_handler)); PRE_MEM_READ( "rt_sigaction(act->sa_mask)", (Addr)&sa->sa_mask, sizeof(sa->sa_mask)); PRE_MEM_READ( "rt_sigaction(act->sa_flags)", (Addr)&sa->sa_flags, sizeof(sa->sa_flags)); if (sa->sa_flags & VKI_SA_RESTORER) PRE_MEM_READ( "rt_sigaction(act->sa_restorer)", (Addr)&sa->sa_restorer, sizeof(sa->sa_restorer)); } if (ARG3 != 0) PRE_MEM_WRITE( "rt_sigaction(oldact)", ARG3, sizeof(struct vki_sigaction)); // XXX: doesn't seem right to be calling do_sys_sigaction for // sys_rt_sigaction... perhaps this function should be renamed // VG_(do_sys_rt_sigaction)() --njn SET_STATUS_from_SysRes( VG_(do_sys_sigaction)(ARG1, (const struct vki_sigaction *)ARG2, (struct vki_sigaction *)ARG3) ); } POST(sys_rt_sigaction) { vg_assert(SUCCESS); if (RES == 0 && ARG3 != 0) POST_MEM_WRITE( ARG3, sizeof(struct vki_sigaction)); } PRE(sys_rt_sigprocmask) { PRINT("sys_rt_sigprocmask ( %d, %p, %p, %llu )",ARG1,ARG2,ARG3,(ULong)ARG4); PRE_REG_READ4(long, "rt_sigprocmask", int, how, vki_sigset_t *, set, vki_sigset_t *, oldset, vki_size_t, sigsetsize); if (ARG2 != 0) PRE_MEM_READ( "rt_sigprocmask(set)", ARG2, sizeof(vki_sigset_t)); if (ARG3 != 0) PRE_MEM_WRITE( "rt_sigprocmask(oldset)", ARG3, sizeof(vki_sigset_t)); // Like the kernel, we fail if the sigsetsize is not exactly what we expect. if (sizeof(vki_sigset_t) != ARG4) SET_STATUS_Failure( VKI_EMFILE ); else { SET_STATUS_from_SysRes( VG_(do_sys_sigprocmask) ( tid, ARG1 /*how*/, (vki_sigset_t*) ARG2, (vki_sigset_t*) ARG3 ) ); } if (SUCCESS) *flags |= SfPollAfter; } POST(sys_rt_sigprocmask) { vg_assert(SUCCESS); if (RES == 0 && ARG3 != 0) POST_MEM_WRITE( ARG3, sizeof(vki_sigset_t)); } PRE(sys_rt_sigpending) { PRINT( "sys_rt_sigpending ( %p )", ARG1 ); PRE_REG_READ2(long, "rt_sigpending", vki_sigset_t *, set, vki_size_t, sigsetsize); PRE_MEM_WRITE( "rt_sigpending(set)", ARG1, sizeof(vki_sigset_t)); } POST(sys_rt_sigpending) { POST_MEM_WRITE( ARG1, sizeof(vki_sigset_t) ) ; } PRE(sys_rt_sigtimedwait) { *flags |= SfMayBlock; PRINT("sys_rt_sigtimedwait ( %p, %p, %p, %lld )", ARG1,ARG2,ARG3,(ULong)ARG4); PRE_REG_READ4(long, "rt_sigtimedwait", const vki_sigset_t *, set, vki_siginfo_t *, info, const struct timespec *, timeout, vki_size_t, sigsetsize); if (ARG1 != 0) PRE_MEM_READ( "rt_sigtimedwait(set)", ARG1, sizeof(vki_sigset_t)); if (ARG2 != 0) PRE_MEM_WRITE( "rt_sigtimedwait(info)", ARG2, sizeof(vki_siginfo_t) ); if (ARG3 != 0) PRE_MEM_READ( "rt_sigtimedwait(timeout)", ARG3, sizeof(struct vki_timespec) ); } POST(sys_rt_sigtimedwait) { if (ARG2 != 0) POST_MEM_WRITE( ARG2, sizeof(vki_siginfo_t) ); } PRE(sys_rt_sigqueueinfo) { PRINT("sys_rt_sigqueueinfo(%d, %d, %p)", ARG1, ARG2, ARG3); PRE_REG_READ3(long, "rt_sigqueueinfo", int, pid, int, sig, vki_siginfo_t *, uinfo); if (ARG2 != 0) PRE_MEM_READ( "rt_sigqueueinfo(uinfo)", ARG3, sizeof(vki_siginfo_t) ); } POST(sys_rt_sigqueueinfo) { if (!ML_(client_signal_OK)(ARG2)) SET_STATUS_Failure( VKI_EINVAL ); } // XXX: x86-specific? The kernel prototypes for the different archs are // hard to decipher. PRE(sys_rt_sigsuspend) { /* The C library interface to sigsuspend just takes a pointer to a signal mask but this system call has two arguments - a pointer to the mask and the number of bytes used by it. The kernel insists on the size being equal to sizeof(sigset_t) however and will just return EINVAL if it isn't. */ *flags |= SfMayBlock; PRINT("sys_rt_sigsuspend ( %p, %d )", ARG1,ARG2 ); PRE_REG_READ2(int, "rt_sigsuspend", vki_sigset_t *, mask, vki_size_t, size) if (ARG1 != (Addr)NULL) { PRE_MEM_READ( "rt_sigsuspend(mask)", ARG1, sizeof(vki_sigset_t) ); } } /* --------------------------------------------------------------------- linux msg* wrapper helpers ------------------------------------------------------------------ */ void ML_(linux_PRE_sys_msgsnd) ( ThreadId tid, UWord arg0, UWord arg1, UWord arg2, UWord arg3 ) { /* int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); */ struct vki_msgbuf *msgp = (struct vki_msgbuf *)arg1; PRE_MEM_READ( "msgsnd(msgp->mtype)", (Addr)&msgp->mtype, sizeof(msgp->mtype) ); PRE_MEM_READ( "msgsnd(msgp->mtext)", (Addr)&msgp->mtext, arg2 ); } void ML_(linux_PRE_sys_msgrcv) ( ThreadId tid, UWord arg0, UWord arg1, UWord arg2, UWord arg3, UWord arg4 ) { /* ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); */ struct vki_msgbuf *msgp = (struct vki_msgbuf *)arg1; PRE_MEM_WRITE( "msgrcv(msgp->mtype)", (Addr)&msgp->mtype, sizeof(msgp->mtype) ); PRE_MEM_WRITE( "msgrcv(msgp->mtext)", (Addr)&msgp->mtext, arg2 ); } void ML_(linux_POST_sys_msgrcv) ( ThreadId tid, UWord res, UWord arg0, UWord arg1, UWord arg2, UWord arg3, UWord arg4 ) { struct vki_msgbuf *msgp = (struct vki_msgbuf *)arg1; POST_MEM_WRITE( (Addr)&msgp->mtype, sizeof(msgp->mtype) ); POST_MEM_WRITE( (Addr)&msgp->mtext, res ); } void ML_(linux_PRE_sys_msgctl) ( ThreadId tid, UWord arg0, UWord arg1, UWord arg2 ) { /* int msgctl(int msqid, int cmd, struct msqid_ds *buf); */ switch (arg1 /* cmd */) { case VKI_IPC_INFO: case VKI_MSG_INFO: case VKI_IPC_INFO|VKI_IPC_64: case VKI_MSG_INFO|VKI_IPC_64: PRE_MEM_WRITE( "msgctl(IPC_INFO, buf)", arg2, sizeof(struct vki_msginfo) ); break; case VKI_IPC_STAT: case VKI_MSG_STAT: PRE_MEM_WRITE( "msgctl(IPC_STAT, buf)", arg2, sizeof(struct vki_msqid_ds) ); break; case VKI_IPC_STAT|VKI_IPC_64: case VKI_MSG_STAT|VKI_IPC_64: PRE_MEM_WRITE( "msgctl(IPC_STAT, arg.buf)", arg2, sizeof(struct vki_msqid64_ds) ); break; case VKI_IPC_SET: PRE_MEM_READ( "msgctl(IPC_SET, arg.buf)", arg2, sizeof(struct vki_msqid_ds) ); break; case VKI_IPC_SET|VKI_IPC_64: PRE_MEM_READ( "msgctl(IPC_SET, arg.buf)", arg2, sizeof(struct vki_msqid64_ds) ); break; } } void ML_(linux_POST_sys_msgctl) ( ThreadId tid, UWord res, UWord arg0, UWord arg1, UWord arg2 ) { switch (arg1 /* cmd */) { case VKI_IPC_INFO: case VKI_MSG_INFO: case VKI_IPC_INFO|VKI_IPC_64: case VKI_MSG_INFO|VKI_IPC_64: POST_MEM_WRITE( arg2, sizeof(struct vki_msginfo) ); break; case VKI_IPC_STAT: case VKI_MSG_STAT: POST_MEM_WRITE( arg2, sizeof(struct vki_msqid_ds) ); break; case VKI_IPC_STAT|VKI_IPC_64: case VKI_MSG_STAT|VKI_IPC_64: POST_MEM_WRITE( arg2, sizeof(struct vki_msqid64_ds) ); break; } } #undef PRE #undef POST /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/