From 9351332329506e1ff11025df3de83bdde175d0cc Mon Sep 17 00:00:00 2001 From: Rhys Kidd Date: Thu, 28 May 2015 12:49:00 +0000 Subject: [PATCH] Signal handler ucontext_t not filled out correctly on OS X bz#341419 Before: == 589 tests, 220 stderr failures, 15 stdout failures, 0 stderrB failures, 0 stdoutB failures, 30 post failures == After: == 591 tests, 220 stderr failures, 15 stdout failures, 0 stderrB failures, 0 stdoutB failures, 30 post failures == git-svn-id: svn://svn.valgrind.org/valgrind/trunk@15296 --- NEWS | 1 + auxprogs/valgrind-di-server.c | 2 + configure.ac | 2 + coregrind/m_sigframe/sigframe-amd64-darwin.c | 119 +++++++--- coregrind/m_sigframe/sigframe-x86-darwin.c | 80 +++++-- include/vki/vki-darwin.h | 6 + none/tests/Makefile.am | 10 +- none/tests/amd64-darwin/Makefile.am | 15 ++ none/tests/amd64-darwin/bug341419.c | 223 +++++++++++++++++++ none/tests/amd64-darwin/bug341419.stderr.exp | 1 + none/tests/amd64-darwin/bug341419.vgtest | 3 + none/tests/amd64-darwin/filter_minimal | 20 ++ none/tests/amd64-darwin/filter_stderr | 11 + none/tests/x86-darwin/Makefile.am | 15 ++ none/tests/x86-darwin/bug341419.c | 203 +++++++++++++++++ none/tests/x86-darwin/bug341419.stderr.exp | 1 + none/tests/x86-darwin/bug341419.vgtest | 3 + none/tests/x86-darwin/filter_stderr | 11 + 18 files changed, 677 insertions(+), 49 deletions(-) create mode 100644 none/tests/amd64-darwin/Makefile.am create mode 100644 none/tests/amd64-darwin/bug341419.c create mode 100644 none/tests/amd64-darwin/bug341419.stderr.exp create mode 100644 none/tests/amd64-darwin/bug341419.vgtest create mode 100755 none/tests/amd64-darwin/filter_minimal create mode 100755 none/tests/amd64-darwin/filter_stderr create mode 100644 none/tests/x86-darwin/Makefile.am create mode 100644 none/tests/x86-darwin/bug341419.c create mode 100644 none/tests/x86-darwin/bug341419.stderr.exp create mode 100644 none/tests/x86-darwin/bug341419.vgtest create mode 100755 none/tests/x86-darwin/filter_stderr diff --git a/NEWS b/NEWS index d482bf980a..357adb972d 100644 --- a/NEWS +++ b/NEWS @@ -139,6 +139,7 @@ where XXXXXX is the bug number as listed below. 340115 Fix none/tests/cmdline[1|2] tests on systems which define TMPDIR 340430 Fix some grammatical weirdness in the manual. 341238 Recognize GCC5/DWARFv5 DW_LANG constants (Go, C11, C++11, C++14) +341419 Signal handler ucontext_t not filled out correctly on OS X 341539 VG_(describe_addr) should not describe address as belonging to client segment if it is past the heap end 341613 Enable building of manythreads and thread-exits tests on Mac OS X diff --git a/auxprogs/valgrind-di-server.c b/auxprogs/valgrind-di-server.c index bfcdd1875e..86c588280c 100644 --- a/auxprogs/valgrind-di-server.c +++ b/auxprogs/valgrind-di-server.c @@ -89,7 +89,9 @@ #include "pub_core_libcfile.h" // For VG_CLO_DEFAULT_LOGPORT /* Needed to get a definition for pread() from unistd.h */ +#ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE 500 +#endif #include #include diff --git a/configure.ac b/configure.ac index 7faeac2991..eac62cd19d 100644 --- a/configure.ac +++ b/configure.ac @@ -3037,6 +3037,8 @@ AC_CONFIG_FILES([ none/tests/darwin/Makefile none/tests/amd64-linux/Makefile none/tests/x86-linux/Makefile + none/tests/amd64-darwin/Makefile + none/tests/x86-darwin/Makefile exp-sgcheck/Makefile exp-sgcheck/tests/Makefile drd/Makefile diff --git a/coregrind/m_sigframe/sigframe-amd64-darwin.c b/coregrind/m_sigframe/sigframe-amd64-darwin.c index 20402bfcfe..1d284bea34 100644 --- a/coregrind/m_sigframe/sigframe-amd64-darwin.c +++ b/coregrind/m_sigframe/sigframe-amd64-darwin.c @@ -45,19 +45,15 @@ #include "pub_core_tooliface.h" #include "pub_core_trampoline.h" #include "pub_core_sigframe.h" /* self */ +#include "priv_sigframe.h" -/* Cheap-ass hack copied from ppc32-aix5 code, just to get started. - Produce a frame with layout entirely of our own choosing. */ +/* Originally copied from ppc32-aix5 code. + Produce a frame with layout entirely of our own choosing. -/* This module creates and removes signal frames for signal deliveries - on amd64-darwin. Kludgey; the machine state ought to be saved in a - ucontext and retrieved from it later, so the handler can modify it - and return. However .. for now .. just stick the vex guest state - in the frame and snarf it again later. - - Also, don't bother with creating siginfo and ucontext in the - handler, although do point them somewhere non-faulting. + This module creates and removes signal frames for signal deliveries + on amd64-darwin. The machine state is saved in a ucontext and retrieved + from it later, so the handler can modify it and return. Frame should have a 16-aligned size, just in case that turns out to be important for Darwin. (be conservative) @@ -66,20 +62,82 @@ struct hacky_sigframe { /* first word looks like a call to a 3-arg amd64-ELF function */ ULong returnAddr; UChar lower_guardzone[512]; // put nothing here - VexGuestAMD64State gst; - VexGuestAMD64State gshadow1; - VexGuestAMD64State gshadow2; + VexGuestAMD64State vex; + VexGuestAMD64State vex_shadow1; + VexGuestAMD64State vex_shadow2; vki_siginfo_t fake_siginfo; struct vki_ucontext fake_ucontext; UInt magicPI; UInt sigNo_private; vki_sigset_t mask; // saved sigmask; restore when hdlr returns - UInt __pad[2]; UChar upper_guardzone[512]; // put nothing here // and don't zero it, since that might overwrite the client's // stack redzone, at least on archs which have one }; +/* Create a plausible-looking sigcontext from the thread's + Vex guest state. NOTE: does not fill in the FP or SSE + bits of sigcontext at the moment. + */ +static void synthesize_ucontext(ThreadState *tst, + struct vki_ucontext *uc, + const struct vki_ucontext *siguc) +{ + VG_(memset)(uc, 0, sizeof(*uc)); + + if (siguc) uc->uc_sigmask = siguc->uc_sigmask; + uc->uc_stack = tst->altstack; + uc->uc_mcontext = &uc->__mcontext_data; + +# define SC2(reg,REG) uc->__mcontext_data.__ss.reg = tst->arch.vex.guest_##REG + SC2(__r8,R8); + SC2(__r9,R9); + SC2(__r10,R10); + SC2(__r11,R11); + SC2(__r12,R12); + SC2(__r13,R13); + SC2(__r14,R14); + SC2(__r15,R15); + SC2(__rdi,RDI); + SC2(__rsi,RSI); + SC2(__rbp,RBP); + SC2(__rbx,RBX); + SC2(__rdx,RDX); + SC2(__rax,RAX); + SC2(__rcx,RCX); + SC2(__rsp,RSP); + SC2(__rip,RIP); + uc->__mcontext_data.__ss.__rflags = LibVEX_GuestAMD64_get_rflags(&tst->arch.vex); + + if (siguc) + uc->__mcontext_data.__es = siguc->__mcontext_data.__es; +# undef SC2 +} + +static void restore_from_ucontext(ThreadState *tst, + const struct vki_ucontext *uc) +{ +# define SC2(REG,reg) tst->arch.vex.guest_##REG = uc->__mcontext_data.__ss.reg + SC2(R8,__r8); + SC2(R9,__r9); + SC2(R10,__r10); + SC2(R11,__r11); + SC2(R12,__r12); + SC2(R13,__r13); + SC2(R14,__r14); + SC2(R15,__r15); + SC2(RDI,__rdi); + SC2(RSI,__rsi); + SC2(RBP,__rbp); + SC2(RBX,__rbx); + SC2(RDX,__rdx); + SC2(RAX,__rax); + SC2(RCX,__rcx); + SC2(RSP,__rsp); + SC2(RIP,__rip); + /* There doesn't seem to be an easy way to restore rflags */ +# undef SC2 +} /* Create a signal frame for thread 'tid'. Make a 3-arg frame regardless of whether the client originally requested a 1-arg @@ -115,26 +173,25 @@ void VG_(sigframe_create) ( ThreadId tid, frame = (struct hacky_sigframe *) rsp; - /* clear it (very conservatively) (why so conservatively??) */ + /* clear it (very conservatively) */ VG_(memset)(&frame->lower_guardzone, 0, sizeof frame->lower_guardzone); - VG_(memset)(&frame->gst, 0, sizeof(VexGuestAMD64State)); - VG_(memset)(&frame->gshadow1, 0, sizeof(VexGuestAMD64State)); - VG_(memset)(&frame->gshadow2, 0, sizeof(VexGuestAMD64State)); + VG_(memset)(&frame->vex, 0, sizeof(VexGuestAMD64State)); + VG_(memset)(&frame->vex_shadow1, 0, sizeof(VexGuestAMD64State)); + VG_(memset)(&frame->vex_shadow2, 0, sizeof(VexGuestAMD64State)); VG_(memset)(&frame->fake_siginfo, 0, sizeof(frame->fake_siginfo)); VG_(memset)(&frame->fake_ucontext, 0, sizeof(frame->fake_ucontext)); /* save stuff in frame */ - frame->gst = tst->arch.vex; - frame->gshadow1 = tst->arch.vex_shadow1; - frame->gshadow2 = tst->arch.vex_shadow2; + frame->vex = tst->arch.vex; + frame->vex_shadow1 = tst->arch.vex_shadow1; + frame->vex_shadow2 = tst->arch.vex_shadow2; frame->sigNo_private = sigNo; frame->mask = tst->sig_mask; frame->magicPI = 0x31415927; - /* Minimally fill in the siginfo and ucontext. Note, utter - lameness prevails. Be underwhelmed, be very underwhelmed. */ - frame->fake_siginfo.si_signo = sigNo; - frame->fake_siginfo.si_code = siginfo->si_code; + /* Fill in the siginfo and ucontext. */ + synthesize_ucontext(tst, &frame->fake_ucontext, siguc); + frame->fake_siginfo = *siginfo; /* Set up stack pointer */ vg_assert(rsp == (Addr)&frame->returnAddr); @@ -152,8 +209,8 @@ void VG_(sigframe_create) ( ThreadId tid, /* XXX should tell the tool that these regs got written */ tst->arch.vex.guest_RDI = (ULong) sigNo; - tst->arch.vex.guest_RSI = (Addr) &frame->fake_siginfo;/* oh well */ - tst->arch.vex.guest_RDX = (Addr) &frame->fake_ucontext; /* oh well */ + tst->arch.vex.guest_RSI = (Addr) &frame->fake_siginfo; + tst->arch.vex.guest_RDX = (Addr) &frame->fake_ucontext; VG_TRACK( post_mem_write, Vg_CoreSignal, tid, (Addr)frame, 1*sizeof(ULong) ); @@ -198,9 +255,11 @@ void VG_(sigframe_destroy)( ThreadId tid, Bool isRT ) /* restore the entire guest state, and shadows, from the frame. Note, as per comments above, this is a kludge - should restore it from saved ucontext. Oh well. */ - tst->arch.vex = frame->gst; - tst->arch.vex_shadow1 = frame->gshadow1; - tst->arch.vex_shadow2 = frame->gshadow2; + tst->arch.vex = frame->vex; + tst->arch.vex_shadow1 = frame->vex_shadow1; + tst->arch.vex_shadow2 = frame->vex_shadow2; + restore_from_ucontext(tst, &frame->fake_ucontext); + tst->sig_mask = frame->mask; tst->tmp_sig_mask = frame->mask; sigNo = frame->sigNo_private; diff --git a/coregrind/m_sigframe/sigframe-x86-darwin.c b/coregrind/m_sigframe/sigframe-x86-darwin.c index 01f87da020..769fdbdc2c 100644 --- a/coregrind/m_sigframe/sigframe-x86-darwin.c +++ b/coregrind/m_sigframe/sigframe-x86-darwin.c @@ -45,19 +45,15 @@ #include "pub_core_tooliface.h" #include "pub_core_trampoline.h" #include "pub_core_sigframe.h" /* self */ +#include "priv_sigframe.h" -/* Cheap-ass hack copied from ppc32-aix5 code, just to get started. - Produce a frame with layout entirely of our own choosing. */ +/* Originally copied from ppc32-aix5 code. + Produce a frame with layout entirely of our own choosing. -/* This module creates and removes signal frames for signal deliveries - on x86-darwin. Kludgey; the machine state ought to be saved in a - ucontext and retrieved from it later, so the handler can modify it - and return. However .. for now .. just stick the vex guest state - in the frame and snarf it again later. - - Also, don't bother with creating siginfo and ucontext in the - handler, although do point them somewhere non-faulting. + This module creates and removes signal frames for signal deliveries + on x86-darwin. The machine state is saved in a ucontext and retrieved + from it later, so the handler can modify it and return. Frame should have a 16-aligned size, just in case that turns out to be important for Darwin. (be conservative) @@ -77,12 +73,59 @@ struct hacky_sigframe { UInt magicPI; UInt sigNo_private; vki_sigset_t mask; // saved sigmask; restore when hdlr returns - UInt __pad[1]; + UInt __pad[3]; UChar upper_guardzone[512]; // put nothing here // and don't zero it, since that might overwrite the client's // stack redzone, at least on archs which have one }; +/* Create a plausible-looking sigcontext from the thread's + Vex guest state. NOTE: does not fill in the FP or SSE + bits of sigcontext at the moment. + */ +static void synthesize_ucontext(ThreadState *tst, + struct vki_ucontext *uc, + const struct vki_ucontext *siguc) +{ + VG_(memset)(uc, 0, sizeof(*uc)); + + if (siguc) uc->uc_sigmask = siguc->uc_sigmask; + uc->uc_stack = tst->altstack; + uc->uc_mcontext = &uc->__mcontext_data; + +# define SC2(reg,REG) uc->__mcontext_data.__ss.reg = tst->arch.vex.guest_##REG + SC2(__edi,EDI); + SC2(__esi,ESI); + SC2(__ebp,EBP); + SC2(__ebx,EBX); + SC2(__edx,EDX); + SC2(__eax,EAX); + SC2(__ecx,ECX); + SC2(__esp,ESP); + SC2(__eip,EIP); + uc->__mcontext_data.__ss.__eflags = LibVEX_GuestX86_get_eflags(&tst->arch.vex); + + if (siguc) + uc->__mcontext_data.__es = siguc->__mcontext_data.__es; +# undef SC2 +} + +static void restore_from_ucontext(ThreadState *tst, + const struct vki_ucontext *uc) +{ +# define SC2(REG,reg) tst->arch.vex.guest_##REG = uc->__mcontext_data.__ss.reg + SC2(EDI,__edi); + SC2(ESI,__esi); + SC2(EBP,__ebp); + SC2(EBX,__ebx); + SC2(EDX,__edx); + SC2(EAX,__eax); + SC2(ECX,__ecx); + SC2(ESP,__esp); + SC2(EIP,__eip); + /* There doesn't seem to be an easy way to restore eflags */ +# undef SC2 +} /* Create a signal frame for thread 'tid'. Make a 3-arg frame regardless of whether the client originally requested a 1-arg @@ -118,7 +161,7 @@ void VG_(sigframe_create) ( ThreadId tid, frame = (struct hacky_sigframe *) esp; - /* clear it (very conservatively) (why so conservatively??) */ + /* clear it (very conservatively) */ VG_(memset)(&frame->lower_guardzone, 0, sizeof frame->lower_guardzone); VG_(memset)(&frame->gst, 0, sizeof(VexGuestX86State)); VG_(memset)(&frame->gshadow1, 0, sizeof(VexGuestX86State)); @@ -134,10 +177,9 @@ void VG_(sigframe_create) ( ThreadId tid, frame->mask = tst->sig_mask; frame->magicPI = 0x31415927; - /* Minimally fill in the siginfo and ucontext. Note, utter - lameness prevails. Be underwhelmed, be very underwhelmed. */ - frame->fake_siginfo.si_signo = sigNo; - frame->fake_siginfo.si_code = siginfo->si_code; + /* Fill in the siginfo and ucontext. */ + synthesize_ucontext(tst, &frame->fake_ucontext, siguc); + frame->fake_siginfo = *siginfo; /* Set up stack pointer */ vg_assert(esp == (Addr)&frame->returnAddr); @@ -153,8 +195,8 @@ void VG_(sigframe_create) ( ThreadId tid, (Addr)frame, 4*sizeof(UInt) ); frame->returnAddr = (UInt)&VG_(x86_darwin_SUBST_FOR_sigreturn); frame->a1_signo = sigNo; - frame->a2_siginfo = (UInt)&frame->fake_siginfo; /* oh well */ - frame->a3_ucontext = (UInt)&frame->fake_ucontext; /* oh well */ + frame->a2_siginfo = (UInt)&frame->fake_siginfo; + frame->a3_ucontext = (UInt)&frame->fake_ucontext; VG_TRACK( post_mem_write, Vg_CoreSignal, tid, (Addr)frame, 4*sizeof(UInt) ); VG_TRACK( post_mem_write, Vg_CoreSignal, tid, @@ -201,6 +243,8 @@ void VG_(sigframe_destroy)( ThreadId tid, Bool isRT ) tst->arch.vex = frame->gst; tst->arch.vex_shadow1 = frame->gshadow1; tst->arch.vex_shadow2 = frame->gshadow2; + restore_from_ucontext(tst, &frame->fake_ucontext); + tst->sig_mask = frame->mask; tst->tmp_sig_mask = frame->mask; sigNo = frame->sigNo_private; diff --git a/include/vki/vki-darwin.h b/include/vki/vki-darwin.h index 9e53f25bfa..7ff7c5bc0a 100644 --- a/include/vki/vki-darwin.h +++ b/include/vki/vki-darwin.h @@ -34,6 +34,12 @@ #ifndef __VKI_DARWIN_H #define __VKI_DARWIN_H +/* struct __darwin_ucontext isn't fully declared without + * this definition. It's crazy but there it is. */ +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE 0500 +#endif + #include #define vki_int8_t int8_t diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am index 1dfcfc498d..c9c9c81d8c 100644 --- a/none/tests/Makefile.am +++ b/none/tests/Makefile.am @@ -52,8 +52,16 @@ if VGCONF_PLATFORMS_INCLUDE_X86_LINUX SUBDIRS += x86-linux endif +if VGCONF_PLATFORMS_INCLUDE_AMD64_DARWIN +SUBDIRS += amd64-darwin +endif +if VGCONF_PLATFORMS_INCLUDE_X86_DARWIN +SUBDIRS += x86-darwin +endif + DIST_SUBDIRS = x86 amd64 ppc32 ppc64 arm arm64 s390x mips32 mips64 tilegx \ - linux darwin amd64-linux x86-linux scripts . + linux darwin amd64-linux x86-linux amd64-darwin \ + x86-darwin scripts . dist_noinst_SCRIPTS = \ filter_cmdline0 \ diff --git a/none/tests/amd64-darwin/Makefile.am b/none/tests/amd64-darwin/Makefile.am new file mode 100644 index 0000000000..b231b30fcb --- /dev/null +++ b/none/tests/amd64-darwin/Makefile.am @@ -0,0 +1,15 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +dist_noinst_SCRIPTS = \ + filter_stderr filter_minimal + +EXTRA_DIST = \ + bug341419.vgtest bug341419.stderr.exp + +check_PROGRAMS = \ + bug341419 + +AM_CFLAGS += @FLAG_M64@ +AM_CXXFLAGS += @FLAG_M64@ +AM_CCASFLAGS += @FLAG_M64@ diff --git a/none/tests/amd64-darwin/bug341419.c b/none/tests/amd64-darwin/bug341419.c new file mode 100644 index 0000000000..e4fdacddbb --- /dev/null +++ b/none/tests/amd64-darwin/bug341419.c @@ -0,0 +1,223 @@ +/* This changes the definition of ucontext_t */ +#define _XOPEN_SOURCE 1 +#include +#include +#include +#include +#include +#include + +#define offsetof(type, fld) ((unsigned long)&((type *)0)->fld) +#define stringify(x) #x + +static int verbose = 0; + +#define _ASSERT_OP(a, op, b) \ + do { \ + unsigned long long _a = (unsigned long long)(a); \ + unsigned long long _b = (unsigned long long)(b); \ + if (verbose) \ + fprintf(stderr, "%s:%d: ASSERT(" stringify(a) \ + " " stringify(op) " " stringify(b) ")\n", \ + __FILE__, __LINE__); \ + if (!(_a op _b)) { \ + fprintf(stderr, "%s:%d: FAILED ASSERT((" stringify(a) \ + "=0x%016llx) " stringify(op) " (" stringify(b) "=0x%016llx))\n", \ + __FILE__, __LINE__, _a, _b); \ + _exit(1); \ + } \ + } while(0) +#define ASSERT_EQ(a, b) _ASSERT_OP(a, ==, b) +#define ASSERT_NE(a, b) _ASSERT_OP(a, !=, b) +#define ASSERT_LTE(a, b) _ASSERT_OP(a, <=, b) +#define ASSERT_GTE(a, b) _ASSERT_OP(a, >=, b) +#define ASSERT(e) \ + do { \ + if (verbose) \ + fprintf(stderr, "%s:%d: ASSERT(" stringify(e) ")\n", \ + __FILE__, __LINE__); \ + if (!(e)) { \ + fprintf(stderr, "%s:%d: FAILED ASSERT(" stringify(e) ")\n", \ + __FILE__, __LINE__); \ + _exit(1); \ + } \ + } while(0) + + +static bool using_int3 = false; +static volatile int sig_count = 0; +static volatile int ran_after_fault = 0; +static void *top_of_stack; +static void *bottom_of_stack; + +void this_function_halts(unsigned long long a0, unsigned long long a1, + unsigned long long a2, unsigned long long a3, + unsigned long long a4, unsigned long long a5) +{ + int foo; + bottom_of_stack = &foo; + + /* Set up registers with known values which will be tested in the signal handler */ + __asm__ volatile("movq $0xfeed01010101cafe,%rax"); + __asm__ volatile("movq $0xfeed02020202cafe,%rbx"); + __asm__ volatile("movq $0xfeed03030303cafe,%r10"); + __asm__ volatile("movq $0xfeed04040404cafe,%r11"); + __asm__ volatile("movq $0xfeed05050505cafe,%r12"); + __asm__ volatile("movq $0xfeed06060606cafe,%r13"); + __asm__ volatile("movq $0xfeed07070707cafe,%r14"); + __asm__ volatile("movq $0xfeed08080808cafe,%r15"); + __asm__ volatile("hlt"); + ran_after_fault++; +} + +void this_function_int3s(unsigned long long a0, unsigned long long a1, + unsigned long long a2, unsigned long long a3, + unsigned long long a4, unsigned long long a5) +{ + int foo; + bottom_of_stack = &foo; + + /* Set up registers with known values which will be tested in the signal handler */ + __asm__ volatile("movq $0xfeed01010101cafe,%rax"); + __asm__ volatile("movq $0xfeed02020202cafe,%rbx"); + __asm__ volatile("movq $0xfeed03030303cafe,%r10"); + __asm__ volatile("movq $0xfeed04040404cafe,%r11"); + __asm__ volatile("movq $0xfeed05050505cafe,%r12"); + __asm__ volatile("movq $0xfeed06060606cafe,%r13"); + __asm__ volatile("movq $0xfeed07070707cafe,%r14"); + __asm__ volatile("movq $0xfeed08080808cafe,%r15"); + __asm__ volatile("int $3"); + ran_after_fault++; +} + + +static void +handle_signal(int sig, siginfo_t *si, void *vuc) +{ + ucontext_t *uc = (ucontext_t *)vuc; + + if (verbose) + { + fprintf(stderr, "handle_signal\n"); + fflush(stderr); + } + + sig_count++; + ASSERT(sig_count == 1); + + int expected_sig = (using_int3 ? SIGTRAP : SIGSEGV); + ASSERT_EQ(sig, expected_sig); + ASSERT_NE(si, NULL); + ASSERT_NE(uc, NULL); + ASSERT_NE(uc->uc_mcontext, NULL); + + /* Test that the siginfo is set up right for this signal */ + ASSERT_EQ(si->si_signo, expected_sig); + ASSERT_EQ(si->si_errno, 0); + int expected_code = (using_int3 ? 1 : 0); + ASSERT_EQ(si->si_code, expected_code); + ASSERT_EQ(si->si_pid, 0); + ASSERT_EQ(si->si_uid, 0); + ASSERT_EQ(si->si_status, 0); + ASSERT_EQ(si->si_addr, 0); + ASSERT_EQ(si->si_band, 0); + + /* Test that RAX is saved to the signal ucontext */ + ASSERT_EQ(uc->uc_mcontext->__ss.__rax, 0xfeed01010101cafe); + + /* Test that the registers used to pass the 1st 6 + * function arguments were saved in the signal ucontext */ + ASSERT_EQ(uc->uc_mcontext->__ss.__rdi, 0xbabe01010101cedeULL); + ASSERT_EQ(uc->uc_mcontext->__ss.__rsi, 0xbabe02020202cedeULL); + ASSERT_EQ(uc->uc_mcontext->__ss.__rdx, 0xbabe03030303cedeULL); + ASSERT_EQ(uc->uc_mcontext->__ss.__rcx, 0xbabe04040404cedeULL); + ASSERT_EQ(uc->uc_mcontext->__ss.__r8, 0xbabe05050505cedeULL); + ASSERT_EQ(uc->uc_mcontext->__ss.__r9, 0xbabe06060606cedeULL); + + /* Test that the saved RBP and RSP point into roughly the right + * part of the stack */ + ASSERT_GTE(uc->uc_mcontext->__ss.__rbp, bottom_of_stack); + ASSERT_LTE(uc->uc_mcontext->__ss.__rbp, top_of_stack); + ASSERT_GTE(uc->uc_mcontext->__ss.__rsp, bottom_of_stack); + ASSERT_LTE(uc->uc_mcontext->__ss.__rsp, top_of_stack); + + /* Test that the saved RIP points into roughly the + * right part of the text segment */ + char *calling_fn = (using_int3 ? (char *)&this_function_int3s : (char *)&this_function_halts); + ASSERT_GTE(uc->uc_mcontext->__ss.__rip, calling_fn); + ASSERT_LTE(uc->uc_mcontext->__ss.__rip, calling_fn+400); + + ASSERT_EQ(uc->uc_mcontext->__ss.__rbx, 0xfeed02020202cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r10, 0xfeed03030303cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r11, 0xfeed04040404cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r12, 0xfeed05050505cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r13, 0xfeed06060606cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r14, 0xfeed07070707cafe); + ASSERT_EQ(uc->uc_mcontext->__ss.__r15, 0xfeed08080808cafe); + /* + printf(" RFLAGS 0x%016llx\n", (unsigned long long)uc->uc_mcontext->__ss.__rflags); + */ + + /* + * Test that the RIP is restored from the signal ucontext; + * this should skip past the HLT/INT instruction and + * allow execution to continue back out to main() + */ + if (verbose) + { + fprintf(stderr, "Setting up to return past the HLT\n"); + fflush(stderr); + } + uc->uc_mcontext->__ss.__rip += (using_int3 ? 0 : 1); + + if (verbose) + { + fprintf(stderr, "Returning from signal handler\n"); + fflush(stderr); + } +} + +int main(int argc, char **argv) +{ + int r; + struct sigaction act; + + top_of_stack = (void *)&act; + + if (argc > 1 && !strcmp(argv[1], "--verbose")) + verbose = 1; + + if (verbose) + printf("Setting up signal handler\n"); + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_signal; + act.sa_flags |= SA_SIGINFO; + if (RUNNING_ON_VALGRIND) + using_int3 = true; + r = sigaction((using_int3 ? SIGTRAP : SIGSEGV), &act, NULL); + ASSERT_EQ(r, 0); + + if (verbose) + { + fprintf(stderr, "Calling function with a breakpoint insn in it\n"); + fflush(stderr); + } + if (using_int3) + this_function_int3s(0xbabe01010101cedeULL, + 0xbabe02020202cedeULL, + 0xbabe03030303cedeULL, + 0xbabe04040404cedeULL, + 0xbabe05050505cedeULL, + 0xbabe06060606cedeULL); + else + this_function_halts(0xbabe01010101cedeULL, + 0xbabe02020202cedeULL, + 0xbabe03030303cedeULL, + 0xbabe04040404cedeULL, + 0xbabe05050505cedeULL, + 0xbabe06060606cedeULL); + ASSERT_EQ(ran_after_fault, 1); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/none/tests/amd64-darwin/bug341419.stderr.exp b/none/tests/amd64-darwin/bug341419.stderr.exp new file mode 100644 index 0000000000..7ef22e9a43 --- /dev/null +++ b/none/tests/amd64-darwin/bug341419.stderr.exp @@ -0,0 +1 @@ +PASS diff --git a/none/tests/amd64-darwin/bug341419.vgtest b/none/tests/amd64-darwin/bug341419.vgtest new file mode 100644 index 0000000000..baae6896ae --- /dev/null +++ b/none/tests/amd64-darwin/bug341419.vgtest @@ -0,0 +1,3 @@ +prog: bug341419 +vgopts: -q +cleanup: rm -f vgcore.* diff --git a/none/tests/amd64-darwin/filter_minimal b/none/tests/amd64-darwin/filter_minimal new file mode 100755 index 0000000000..e69398ce8a --- /dev/null +++ b/none/tests/amd64-darwin/filter_minimal @@ -0,0 +1,20 @@ +#! /bin/sh + +dir=`dirname $0` + +# Remove ==pid== and **pid** strings +perl -p -e 's/(==|\*\*)[0-9]{1,7}\1 //' | + +perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g' | + +# Older bash versions print abnormal termination messages on the stderr +# of the bash process. Newer bash versions redirect such messages properly. +# Suppress any redirected abnormal termination messages. You can find the +# complete list of messages in the bash source file siglist.c. +perl -n -e 'print if !/^(Segmentation fault|Alarm clock|Aborted|Bus error)( \(core dumped\))?$/' | + +# Remove the size in "The main thread stack size..." message. +sed "s/The main thread stack size used in this run was [0-9]*/The main thread stack size used in this run was .../" + +# NOTE: it is essential for the bug345887 testcase that the stderr +# filtering does *not* remove lines beginning with -- diff --git a/none/tests/amd64-darwin/filter_stderr b/none/tests/amd64-darwin/filter_stderr new file mode 100755 index 0000000000..587754a136 --- /dev/null +++ b/none/tests/amd64-darwin/filter_stderr @@ -0,0 +1,11 @@ +#! /bin/sh + +dir=`dirname $0` + +# Remove ==pid== and --pid-- and **pid** strings +perl -p -e 's/(==|--|\*\*)[0-9]{1,7}\1 //' | + +perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g' + +# NOTE: it is essential for the bug345887 testcase that the stderr +# filtering does *not* remove lines beginning with -- diff --git a/none/tests/x86-darwin/Makefile.am b/none/tests/x86-darwin/Makefile.am new file mode 100644 index 0000000000..0b76f950ae --- /dev/null +++ b/none/tests/x86-darwin/Makefile.am @@ -0,0 +1,15 @@ + +include $(top_srcdir)/Makefile.tool-tests.am + +dist_noinst_SCRIPTS = \ + filter_stderr + +EXTRA_DIST = \ + bug341419.vgtest bug341419.stderr.exp + +check_PROGRAMS = \ + bug341419 + +AM_CFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) +AM_CXXFLAGS += @FLAG_M32@ $(FLAG_MMMX) $(FLAG_MSSE) +AM_CCASFLAGS += @FLAG_M32@ diff --git a/none/tests/x86-darwin/bug341419.c b/none/tests/x86-darwin/bug341419.c new file mode 100644 index 0000000000..ef2d52efd4 --- /dev/null +++ b/none/tests/x86-darwin/bug341419.c @@ -0,0 +1,203 @@ +/* This changes the definition of ucontext_t */ +#define _XOPEN_SOURCE 1 +#include +#include +#include +#include +#include +#include + +#define offsetof(type, fld) ((unsigned long)&((type *)0)->fld) +#define stringify(x) #x + +static int verbose = 0; + +#define _ASSERT_OP(a, op, b) \ + do { \ + unsigned long long _a = (unsigned long long)(a); \ + unsigned long long _b = (unsigned long long)(b); \ + if (verbose) \ + fprintf(stderr, "%s:%d: ASSERT(" stringify(a) \ + " " stringify(op) " " stringify(b) ")\n", \ + __FILE__, __LINE__); \ + if (!(_a op _b)) { \ + fprintf(stderr, "%s:%d: FAILED ASSERT((" stringify(a) \ + "=0x%016llx) " stringify(op) " (" stringify(b) "=0x%016llx))\n", \ + __FILE__, __LINE__, _a, _b); \ + _exit(1); \ + } \ + } while(0) +#define ASSERT_EQ(a, b) _ASSERT_OP(a, ==, b) +#define ASSERT_NE(a, b) _ASSERT_OP(a, !=, b) +#define ASSERT_LTE(a, b) _ASSERT_OP(a, <=, b) +#define ASSERT_GTE(a, b) _ASSERT_OP(a, >=, b) +#define ASSERT(e) \ + do { \ + if (verbose) \ + fprintf(stderr, "%s:%d: ASSERT(" stringify(e) ")\n", \ + __FILE__, __LINE__); \ + if (!(e)) { \ + fprintf(stderr, "%s:%d: FAILED ASSERT(" stringify(e) ")\n", \ + __FILE__, __LINE__); \ + _exit(1); \ + } \ + } while(0) + + +static bool using_int3 = false; +static volatile int sig_count = 0; +static volatile int ran_after_fault = 0; +static void *top_of_stack; +static void *bottom_of_stack; + +void this_function_halts(void) +{ + int foo; + bottom_of_stack = &foo - 4; + /* EAX is used by compiler-generated code to + * increment ran_after_fault, so we need to + * preserve it in our asm section */ + unsigned long saved_eax; + + /* Set up registers with known values which will be tested in the signal handler */ + __asm__ volatile("movl %%eax, %0" : "=m"(saved_eax)); + __asm__ volatile("movl $0xfeed0101,%eax"); + __asm__ volatile("movl $0xfeed0202,%ebx"); + __asm__ volatile("movl $0xfeed0303,%ecx"); + __asm__ volatile("movl $0xfeed0404,%edx"); + __asm__ volatile("movl $0xfeed0505,%edi"); + __asm__ volatile("movl $0xfeed0606,%esi"); + __asm__ volatile("hlt"); + __asm__ volatile("movl %0, %%eax" : : "m"(saved_eax)); + ran_after_fault++; +} + +void this_function_int3s(void) +{ + int foo; + bottom_of_stack = &foo - 4; + unsigned long saved_eax; + + /* Set up registers with known values which will be tested in the signal handler */ + __asm__ volatile("movl %%eax, %0" : "=m"(saved_eax)); + __asm__ volatile("movl $0xfeed0101,%eax"); + __asm__ volatile("movl $0xfeed0202,%ebx"); + __asm__ volatile("movl $0xfeed0303,%ecx"); + __asm__ volatile("movl $0xfeed0404,%edx"); + __asm__ volatile("movl $0xfeed0505,%edi"); + __asm__ volatile("movl $0xfeed0606,%esi"); + __asm__ volatile("int $3"); + __asm__ volatile("movl %0, %%eax" : : "m"(saved_eax)); + ran_after_fault++; +} + + +static void +handle_signal(int sig, siginfo_t *si, void *vuc) +{ + ucontext_t *uc = (ucontext_t *)vuc; + + if (verbose) + { + fprintf(stderr, "handle_signal\n"); + fflush(stderr); + } + + sig_count++; + ASSERT(sig_count == 1); + + int expected_sig = (using_int3 ? SIGTRAP : SIGSEGV); + ASSERT_EQ(sig, expected_sig); + ASSERT_NE(si, NULL); + ASSERT_NE(uc, NULL); + ASSERT_NE(uc->uc_mcontext, NULL); + + /* Test that the siginfo is set up right for this signal */ + ASSERT_EQ(si->si_signo, expected_sig); + ASSERT_EQ(si->si_errno, 0); + int expected_code = (using_int3 ? 1 : 0); + ASSERT_EQ(si->si_code, expected_code); + ASSERT_EQ(si->si_pid, 0); + ASSERT_EQ(si->si_uid, 0); + ASSERT_EQ(si->si_status, 0); + ASSERT_EQ(si->si_addr, 0); + ASSERT_EQ(si->si_band, 0); + + /* Test that various registers were saved in the signal ucontext */ + ASSERT_EQ(uc->uc_mcontext->__ss.__eax, 0xfeed0101); + ASSERT_EQ(uc->uc_mcontext->__ss.__ebx, 0xfeed0202); + ASSERT_EQ(uc->uc_mcontext->__ss.__ecx, 0xfeed0303); + ASSERT_EQ(uc->uc_mcontext->__ss.__edx, 0xfeed0404); + ASSERT_EQ(uc->uc_mcontext->__ss.__edi, 0xfeed0505); + ASSERT_EQ(uc->uc_mcontext->__ss.__esi, 0xfeed0606); + + /* Test that the saved EBP and ESP point into roughly the right + * part of the stack */ + ASSERT_GTE(uc->uc_mcontext->__ss.__ebp, bottom_of_stack); + ASSERT_LTE(uc->uc_mcontext->__ss.__ebp, top_of_stack); + ASSERT_GTE(uc->uc_mcontext->__ss.__esp, bottom_of_stack); + ASSERT_LTE(uc->uc_mcontext->__ss.__esp, top_of_stack); + + /* Test that the saved EIP points into roughly the + * right part of the text segment */ + char *calling_fn = (using_int3 ? (char *)&this_function_int3s : (char *)&this_function_halts); + ASSERT_GTE(uc->uc_mcontext->__ss.__eip, calling_fn); + ASSERT_LTE(uc->uc_mcontext->__ss.__eip, calling_fn+400); + + /* + printf(" RFLAGS 0x%016llx\n", (unsigned long long)uc->uc_mcontext->__ss.__rflags); + */ + + /* + * Test that the EIP is restored from the signal ucontext; + * this should skip past the HLT/INT instruction and + * allow execution to continue back out to main() + */ + if (verbose) + { + fprintf(stderr, "Setting up to return past the HLT\n"); + fflush(stderr); + } + uc->uc_mcontext->__ss.__eip += (using_int3 ? 0 : 1); + + if (verbose) + { + fprintf(stderr, "Returning from signal handler\n"); + fflush(stderr); + } +} + +int main(int argc, char **argv) +{ + int r; + struct sigaction act; + + top_of_stack = (void *)&act; + + if (argc > 1 && !strcmp(argv[1], "--verbose")) + verbose = 1; + + if (verbose) + printf("Setting up signal handler\n"); + memset(&act, 0, sizeof(act)); + act.sa_sigaction = handle_signal; + act.sa_flags |= SA_SIGINFO; + if (RUNNING_ON_VALGRIND) + using_int3 = true; + r = sigaction((using_int3 ? SIGTRAP : SIGSEGV), &act, NULL); + ASSERT_EQ(r, 0); + + if (verbose) + { + fprintf(stderr, "Calling function with a breakpoint insn in it\n"); + fflush(stderr); + } + if (using_int3) + this_function_int3s(); + else + this_function_halts(); + ASSERT_EQ(ran_after_fault, 1); + + fprintf(stderr, "PASS\n"); + return 0; +} diff --git a/none/tests/x86-darwin/bug341419.stderr.exp b/none/tests/x86-darwin/bug341419.stderr.exp new file mode 100644 index 0000000000..7ef22e9a43 --- /dev/null +++ b/none/tests/x86-darwin/bug341419.stderr.exp @@ -0,0 +1 @@ +PASS diff --git a/none/tests/x86-darwin/bug341419.vgtest b/none/tests/x86-darwin/bug341419.vgtest new file mode 100644 index 0000000000..baae6896ae --- /dev/null +++ b/none/tests/x86-darwin/bug341419.vgtest @@ -0,0 +1,3 @@ +prog: bug341419 +vgopts: -q +cleanup: rm -f vgcore.* diff --git a/none/tests/x86-darwin/filter_stderr b/none/tests/x86-darwin/filter_stderr new file mode 100755 index 0000000000..587754a136 --- /dev/null +++ b/none/tests/x86-darwin/filter_stderr @@ -0,0 +1,11 @@ +#! /bin/sh + +dir=`dirname $0` + +# Remove ==pid== and --pid-- and **pid** strings +perl -p -e 's/(==|--|\*\*)[0-9]{1,7}\1 //' | + +perl -p -e 's/0x[0-9A-Fa-f]+/0x......../g' + +# NOTE: it is essential for the bug345887 testcase that the stderr +# filtering does *not* remove lines beginning with -- -- 2.47.3