]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Implement sigaltstack. Most of the logic is copied more-or-less
authorJulian Seward <jseward@acm.org>
Wed, 22 May 2002 23:34:20 +0000 (23:34 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 22 May 2002 23:34:20 +0000 (23:34 +0000)
verbatim from the Linux kernel sources, which has to be a good thing.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@309

coregrind/vg_include.h
coregrind/vg_kerneliface.h
coregrind/vg_signals.c
tests/Makefile.am
tests/sigaltstack.c [new file with mode: 0644]
vg_include.h
vg_kerneliface.h
vg_signals.c
vg_syscall_mem.c

index 692a48c528f0e91ecf9f1c0f03355b8f415fef73..9431c8e71a31e70b2ced769210cfe934f3220f7a 100644 (file)
@@ -705,6 +705,7 @@ extern void VG_(handle_SCSS_change) ( Bool force_update );
 
 
 /* Fake system calls for signal handling. */
+extern void VG_(do__NR_sigaltstack)   ( ThreadId tid );
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
 extern void VG_(do__NR_sigprocmask)   ( ThreadId tid,
                                         Int how, 
index 45f8028d1a0c6647890cb8ead22361ad55b2eb24..ef74d8db2995f20117bb1186ca0422828d4f8b8d 100644 (file)
@@ -90,6 +90,13 @@ typedef
    vki_kstack_t;
 
 
+/* sigaltstack controls */
+#define VKI_SS_ONSTACK      1
+#define VKI_SS_DISABLE      2
+
+#define VKI_MINSIGSTKSZ     2048
+#define VKI_SIGSTKSZ        8192
+
 
 
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
@@ -135,6 +142,7 @@ typedef
 
 /* Copied from /usr/src/linux-2.4.9-13/include/asm/errno.h */
 
+#define VKI_EPERM            1      /* Operation not permitted */
 #define VKI_EINTR            4      /* Interrupted system call */
 #define VKI_EINVAL          22      /* Invalid argument */
 #define VKI_ENOMEM          12      /* Out of memory */
index 677b272a53a84f7f714ea9b99ea74de8efe5d148..1b4c98128c56ac4d854827887e2d39cbd0eeb504 100644 (file)
@@ -83,7 +83,12 @@ typedef
 
 typedef 
    struct {
+      /* per-signal info */
       SCSS_Per_Signal scss_per_sig[1+VKI_KNSIG];
+
+      /* Signal delivery stack, if any. */
+      vki_kstack_t altstack;
+
       /* Additional elements to SCSS not stored here:
          - for each thread, the thread's blocking mask
          - for each thread in WaitSIG, the set of waited-on sigs
@@ -291,11 +296,13 @@ void calculate_SKSS_from_SCSS ( SKSS* dst )
             ("sigactions with SA_NOMASK");
       vg_assert(!(scss_flags & VKI_SA_NOMASK));
       */
-      /* SA_ONSTACK: not allowed */
+      /* SA_ONSTACK: client setting is irrelevant here */
+      /*
       if (scss_flags & VKI_SA_ONSTACK)
          VG_(unimplemented)
             ("signals on an alternative stack (SA_ONSTACK)");
       vg_assert(!(scss_flags & VKI_SA_ONSTACK));
+      */
       /* ... but WE ask for on-stack ourselves ... */
       skss_flags |= VKI_SA_ONSTACK;
 
@@ -442,6 +449,74 @@ void VG_(handle_SCSS_change) ( Bool force_update )
    Update/query SCSS in accordance with client requests.
    ------------------------------------------------------------------ */
 
+/* Logic for this alt-stack stuff copied directly from do_sigaltstack
+   in kernel/signal.[ch] */
+
+/* True if we are on the alternate signal stack.  */
+static Int on_sig_stack ( Addr m_esp )
+{
+   return (m_esp - (Addr)vg_scss.altstack.ss_sp 
+           < vg_scss.altstack.ss_size);
+}
+
+static Int sas_ss_flags ( Addr m_esp )
+{
+   return (vg_scss.altstack.ss_size == 0 
+              ? VKI_SS_DISABLE
+              : on_sig_stack(m_esp) ? VKI_SS_ONSTACK : 0);
+}
+
+
+void VG_(do__NR_sigaltstack) ( ThreadId tid )
+{
+   vki_kstack_t* ss;
+   vki_kstack_t* oss;
+   Addr          m_esp;
+
+   vg_assert(VG_(is_valid_tid)(tid));
+   ss    = (vki_kstack_t*)(VG_(threads)[tid].m_ebx);
+   oss   = (vki_kstack_t*)(VG_(threads)[tid].m_ecx);
+   m_esp = VG_(threads)[tid].m_esp;
+
+   if (VG_(clo_trace_signals))
+      VG_(message)(Vg_DebugExtraMsg, 
+         "__NR_sigaltstack: tid %d, "
+         "ss 0x%x, oss 0x%x (current %%esp %p)",
+         tid, (UInt)ss, (UInt)oss, (UInt)m_esp );
+
+   if (oss != NULL) {
+      oss->ss_sp    = vg_scss.altstack.ss_sp;
+      oss->ss_size  = vg_scss.altstack.ss_size;
+      oss->ss_flags = sas_ss_flags(m_esp);
+   }
+
+   if (ss != NULL) {
+      if (on_sig_stack(VG_(threads)[tid].m_esp)) {
+         SET_EAX(tid, -VKI_EPERM);
+         return;
+      }
+      if (ss->ss_flags != VKI_SS_DISABLE 
+          && ss->ss_flags != VKI_SS_ONSTACK 
+          && ss->ss_flags != 0) {
+         SET_EAX(tid, -VKI_EINVAL);
+         return;
+      }
+      if (ss->ss_flags == VKI_SS_DISABLE) {
+         vg_scss.altstack.ss_size = 0;
+         vg_scss.altstack.ss_sp = NULL;
+      } else {
+         if (ss->ss_size < VKI_MINSIGSTKSZ) {
+            SET_EAX(tid, -VKI_ENOMEM);
+            return;
+         }
+      }
+      vg_scss.altstack.ss_sp   = ss->ss_sp;
+      vg_scss.altstack.ss_size = ss->ss_size;
+   }
+   SET_EAX(tid, 0);
+}
+
+
 void VG_(do__NR_sigaction) ( ThreadId tid )
 {
    Int              signo;
@@ -757,20 +832,38 @@ static
 void vg_push_signal_frame ( ThreadId tid, int sigNo )
 {
    Int          i;
-   Addr         esp;
+   Addr         esp, esp_top_of_frame;
    VgSigFrame*  frame;
    ThreadState* tst;
 
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
    vg_assert(VG_(is_valid_tid)(tid));
    tst = & VG_(threads)[tid];
-   esp = tst->m_esp;
 
+   if (/* this signal asked to run on an alt stack */
+       (vg_scss.scss_per_sig[sigNo].scss_flags & VKI_SA_ONSTACK)
+       && /* there is a defined and enabled alt stack, which we're not
+             already using.  Logic from get_sigframe in
+             arch/i386/kernel/signal.c. */
+          sas_ss_flags(tst->m_esp) == 0
+      ) {
+      esp_top_of_frame 
+         = (Addr)(vg_scss.altstack.ss_sp) + vg_scss.altstack.ss_size;
+      if (VG_(clo_trace_signals))
+         VG_(message)(Vg_DebugMsg,
+            "delivering signal %d to thread %d: on ALT STACK", 
+            sigNo, tid );
+   } else {
+      esp_top_of_frame = tst->m_esp;
+   }
+
+   esp = esp_top_of_frame;
    esp -= sizeof(VgSigFrame);
    frame = (VgSigFrame*)esp;
    /* Assert that the frame is placed correctly. */
    vg_assert( (sizeof(VgSigFrame) & 0x3) == 0 );
    vg_assert( ((Char*)(&frame->magicE)) + sizeof(UInt) 
-              == ((Char*)(tst->m_esp)) );
+              == ((Char*)(esp_top_of_frame)) );
 
    frame->retaddr    = (UInt)(&VG_(signalreturn_bogusRA));
    frame->sigNo      = sigNo;
@@ -845,16 +938,9 @@ Int vg_pop_signal_frame ( ThreadId tid )
    for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
       tst->m_fpu[i] = frame->fpustate[i];
 
-   /* Mark the frame structure as nonaccessible.  Has to happen
-      _before_ vg_m_state.m_esp is given a new value.
-      handle_esp_assignment reads %ESP from baseBlock, so we park it
-      there first.  Re-place the junk there afterwards. */
-   if (VG_(clo_instrument)) {
-      vg_assert(VG_(baseBlock)[VGOFF_(m_esp)] == 0xDEADBEEF);
-      VG_(baseBlock)[VGOFF_(m_esp)] = tst->m_esp;
-      VGM_(handle_esp_assignment) ( frame->esp );
-      VG_(baseBlock)[VGOFF_(m_esp)] = 0xDEADBEEF;
-   }
+   /* Mark the frame structure as nonaccessible. */
+   if (VG_(clo_instrument))
+      VGM_(make_noaccess)( (Addr)frame, sizeof(VgSigFrame) );
 
    /* Restore machine state from the saved context. */
    tst->m_eax     = frame->eax;
@@ -1234,6 +1320,10 @@ void VG_(sigstartup_actions) ( void )
       vg_scss.scss_per_sig[i].scss_restorer = sa.ksa_restorer;
    }
 
+   /* Copy the alt stack, if any. */
+   ret = VG_(ksigaltstack)(NULL, &vg_scss.altstack);
+   vg_assert(ret == 0);
+
    /* Copy the process' signal mask into the root thread. */
    vg_assert(VG_(threads)[1].status == VgTs_Runnable);
    VG_(threads)[1].sig_mask = saved_procmask;
@@ -1306,6 +1396,10 @@ void VG_(sigshutdown_actions) ( void )
 
    }
 
+   /* Restore the sig alt stack. */
+   ret = VG_(ksigaltstack)(&vg_scss.altstack, NULL);
+   vg_assert(ret == 0);
+
    /* A bit of a kludge -- set the sigmask to that of the root
       thread. */
    vg_assert(VG_(threads)[1].status != VgTs_Empty);
index 796a0334dbd71d5257e69f8e08e32543c2b56caf..0f0417f100470aaf882d0e1c3474be2da8635430 100644 (file)
@@ -28,4 +28,5 @@ EXTRA_DIST = \
        bt_everything.c bt_literal.c \
        pth_threadpool.c pth_specific.c pth_mutexspeed.c malloc3.c \
        pth_once.c weirdioctl.c pth_signal1.c pth_signal2.c \
-       discard.c pth_semaphore1.c new_override.cpp pth_yield.c
+       discard.c pth_semaphore1.c new_override.cpp pth_yield.c \
+       sigaltstack
diff --git a/tests/sigaltstack.c b/tests/sigaltstack.c
new file mode 100644 (file)
index 0000000..8f31e68
--- /dev/null
@@ -0,0 +1,38 @@
+
+
+#include <stdio.h>
+#include <malloc.h>
+#include <signal.h>
+
+void sig_handler(int sig){
+  int var;
+  fprintf(stderr, "caught signal, local var is on %p\n", &var);
+}
+
+
+int main(int argv, char** argc) {
+  int res, i;
+  stack_t sigstk;
+  struct sigaction act;
+  sigstk.ss_sp = (char *)malloc(SIGSTKSZ);
+
+  sigstk.ss_size = SIGSTKSZ;
+  sigstk.ss_flags = 0;
+  fprintf(stderr, "calling sigaltstack, stack base is %p\n", sigstk.ss_sp);
+  if (sigaltstack(&sigstk,0)<0) perror("sigaltstack");
+
+  fprintf(stderr,"setting sigaction\n");
+  act.sa_flags=SA_ONSTACK;
+  act.sa_handler=&sig_handler;
+  res = sigaction(SIGUSR1,&act,0);
+  fprintf(stderr, "res = %d\n", res);
+  fprintf(stderr, "raising the signal\n");
+  raise(SIGUSR1);
+  
+  /* Loop long enough so valgrind has a forced context switch and
+     actually delivers the signal before the thread exits. */
+  for (i = 0; i < 1000000; i++) ;
+
+  fprintf(stderr, "done\n");
+  return 0;
+}
index 692a48c528f0e91ecf9f1c0f03355b8f415fef73..9431c8e71a31e70b2ced769210cfe934f3220f7a 100644 (file)
@@ -705,6 +705,7 @@ extern void VG_(handle_SCSS_change) ( Bool force_update );
 
 
 /* Fake system calls for signal handling. */
+extern void VG_(do__NR_sigaltstack)   ( ThreadId tid );
 extern void VG_(do__NR_sigaction)     ( ThreadId tid );
 extern void VG_(do__NR_sigprocmask)   ( ThreadId tid,
                                         Int how, 
index 45f8028d1a0c6647890cb8ead22361ad55b2eb24..ef74d8db2995f20117bb1186ca0422828d4f8b8d 100644 (file)
@@ -90,6 +90,13 @@ typedef
    vki_kstack_t;
 
 
+/* sigaltstack controls */
+#define VKI_SS_ONSTACK      1
+#define VKI_SS_DISABLE      2
+
+#define VKI_MINSIGSTKSZ     2048
+#define VKI_SIGSTKSZ        8192
+
 
 
 #define VKI_SIG_BLOCK          0    /* for blocking signals */
@@ -135,6 +142,7 @@ typedef
 
 /* Copied from /usr/src/linux-2.4.9-13/include/asm/errno.h */
 
+#define VKI_EPERM            1      /* Operation not permitted */
 #define VKI_EINTR            4      /* Interrupted system call */
 #define VKI_EINVAL          22      /* Invalid argument */
 #define VKI_ENOMEM          12      /* Out of memory */
index 677b272a53a84f7f714ea9b99ea74de8efe5d148..1b4c98128c56ac4d854827887e2d39cbd0eeb504 100644 (file)
@@ -83,7 +83,12 @@ typedef
 
 typedef 
    struct {
+      /* per-signal info */
       SCSS_Per_Signal scss_per_sig[1+VKI_KNSIG];
+
+      /* Signal delivery stack, if any. */
+      vki_kstack_t altstack;
+
       /* Additional elements to SCSS not stored here:
          - for each thread, the thread's blocking mask
          - for each thread in WaitSIG, the set of waited-on sigs
@@ -291,11 +296,13 @@ void calculate_SKSS_from_SCSS ( SKSS* dst )
             ("sigactions with SA_NOMASK");
       vg_assert(!(scss_flags & VKI_SA_NOMASK));
       */
-      /* SA_ONSTACK: not allowed */
+      /* SA_ONSTACK: client setting is irrelevant here */
+      /*
       if (scss_flags & VKI_SA_ONSTACK)
          VG_(unimplemented)
             ("signals on an alternative stack (SA_ONSTACK)");
       vg_assert(!(scss_flags & VKI_SA_ONSTACK));
+      */
       /* ... but WE ask for on-stack ourselves ... */
       skss_flags |= VKI_SA_ONSTACK;
 
@@ -442,6 +449,74 @@ void VG_(handle_SCSS_change) ( Bool force_update )
    Update/query SCSS in accordance with client requests.
    ------------------------------------------------------------------ */
 
+/* Logic for this alt-stack stuff copied directly from do_sigaltstack
+   in kernel/signal.[ch] */
+
+/* True if we are on the alternate signal stack.  */
+static Int on_sig_stack ( Addr m_esp )
+{
+   return (m_esp - (Addr)vg_scss.altstack.ss_sp 
+           < vg_scss.altstack.ss_size);
+}
+
+static Int sas_ss_flags ( Addr m_esp )
+{
+   return (vg_scss.altstack.ss_size == 0 
+              ? VKI_SS_DISABLE
+              : on_sig_stack(m_esp) ? VKI_SS_ONSTACK : 0);
+}
+
+
+void VG_(do__NR_sigaltstack) ( ThreadId tid )
+{
+   vki_kstack_t* ss;
+   vki_kstack_t* oss;
+   Addr          m_esp;
+
+   vg_assert(VG_(is_valid_tid)(tid));
+   ss    = (vki_kstack_t*)(VG_(threads)[tid].m_ebx);
+   oss   = (vki_kstack_t*)(VG_(threads)[tid].m_ecx);
+   m_esp = VG_(threads)[tid].m_esp;
+
+   if (VG_(clo_trace_signals))
+      VG_(message)(Vg_DebugExtraMsg, 
+         "__NR_sigaltstack: tid %d, "
+         "ss 0x%x, oss 0x%x (current %%esp %p)",
+         tid, (UInt)ss, (UInt)oss, (UInt)m_esp );
+
+   if (oss != NULL) {
+      oss->ss_sp    = vg_scss.altstack.ss_sp;
+      oss->ss_size  = vg_scss.altstack.ss_size;
+      oss->ss_flags = sas_ss_flags(m_esp);
+   }
+
+   if (ss != NULL) {
+      if (on_sig_stack(VG_(threads)[tid].m_esp)) {
+         SET_EAX(tid, -VKI_EPERM);
+         return;
+      }
+      if (ss->ss_flags != VKI_SS_DISABLE 
+          && ss->ss_flags != VKI_SS_ONSTACK 
+          && ss->ss_flags != 0) {
+         SET_EAX(tid, -VKI_EINVAL);
+         return;
+      }
+      if (ss->ss_flags == VKI_SS_DISABLE) {
+         vg_scss.altstack.ss_size = 0;
+         vg_scss.altstack.ss_sp = NULL;
+      } else {
+         if (ss->ss_size < VKI_MINSIGSTKSZ) {
+            SET_EAX(tid, -VKI_ENOMEM);
+            return;
+         }
+      }
+      vg_scss.altstack.ss_sp   = ss->ss_sp;
+      vg_scss.altstack.ss_size = ss->ss_size;
+   }
+   SET_EAX(tid, 0);
+}
+
+
 void VG_(do__NR_sigaction) ( ThreadId tid )
 {
    Int              signo;
@@ -757,20 +832,38 @@ static
 void vg_push_signal_frame ( ThreadId tid, int sigNo )
 {
    Int          i;
-   Addr         esp;
+   Addr         esp, esp_top_of_frame;
    VgSigFrame*  frame;
    ThreadState* tst;
 
+   vg_assert(sigNo >= 1 && sigNo <= VKI_KNSIG);
    vg_assert(VG_(is_valid_tid)(tid));
    tst = & VG_(threads)[tid];
-   esp = tst->m_esp;
 
+   if (/* this signal asked to run on an alt stack */
+       (vg_scss.scss_per_sig[sigNo].scss_flags & VKI_SA_ONSTACK)
+       && /* there is a defined and enabled alt stack, which we're not
+             already using.  Logic from get_sigframe in
+             arch/i386/kernel/signal.c. */
+          sas_ss_flags(tst->m_esp) == 0
+      ) {
+      esp_top_of_frame 
+         = (Addr)(vg_scss.altstack.ss_sp) + vg_scss.altstack.ss_size;
+      if (VG_(clo_trace_signals))
+         VG_(message)(Vg_DebugMsg,
+            "delivering signal %d to thread %d: on ALT STACK", 
+            sigNo, tid );
+   } else {
+      esp_top_of_frame = tst->m_esp;
+   }
+
+   esp = esp_top_of_frame;
    esp -= sizeof(VgSigFrame);
    frame = (VgSigFrame*)esp;
    /* Assert that the frame is placed correctly. */
    vg_assert( (sizeof(VgSigFrame) & 0x3) == 0 );
    vg_assert( ((Char*)(&frame->magicE)) + sizeof(UInt) 
-              == ((Char*)(tst->m_esp)) );
+              == ((Char*)(esp_top_of_frame)) );
 
    frame->retaddr    = (UInt)(&VG_(signalreturn_bogusRA));
    frame->sigNo      = sigNo;
@@ -845,16 +938,9 @@ Int vg_pop_signal_frame ( ThreadId tid )
    for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++)
       tst->m_fpu[i] = frame->fpustate[i];
 
-   /* Mark the frame structure as nonaccessible.  Has to happen
-      _before_ vg_m_state.m_esp is given a new value.
-      handle_esp_assignment reads %ESP from baseBlock, so we park it
-      there first.  Re-place the junk there afterwards. */
-   if (VG_(clo_instrument)) {
-      vg_assert(VG_(baseBlock)[VGOFF_(m_esp)] == 0xDEADBEEF);
-      VG_(baseBlock)[VGOFF_(m_esp)] = tst->m_esp;
-      VGM_(handle_esp_assignment) ( frame->esp );
-      VG_(baseBlock)[VGOFF_(m_esp)] = 0xDEADBEEF;
-   }
+   /* Mark the frame structure as nonaccessible. */
+   if (VG_(clo_instrument))
+      VGM_(make_noaccess)( (Addr)frame, sizeof(VgSigFrame) );
 
    /* Restore machine state from the saved context. */
    tst->m_eax     = frame->eax;
@@ -1234,6 +1320,10 @@ void VG_(sigstartup_actions) ( void )
       vg_scss.scss_per_sig[i].scss_restorer = sa.ksa_restorer;
    }
 
+   /* Copy the alt stack, if any. */
+   ret = VG_(ksigaltstack)(NULL, &vg_scss.altstack);
+   vg_assert(ret == 0);
+
    /* Copy the process' signal mask into the root thread. */
    vg_assert(VG_(threads)[1].status == VgTs_Runnable);
    VG_(threads)[1].sig_mask = saved_procmask;
@@ -1306,6 +1396,10 @@ void VG_(sigshutdown_actions) ( void )
 
    }
 
+   /* Restore the sig alt stack. */
+   ret = VG_(ksigaltstack)(&vg_scss.altstack, NULL);
+   vg_assert(ret == 0);
+
    /* A bit of a kludge -- set the sigmask to that of the root
       thread. */
    vg_assert(VG_(threads)[1].status != VgTs_Empty);
index 019d43103ba04d8fe4175c1e3ff31a25d0523207..3cffd775b72f6f3e49f1b9230fb1da6a4bc9bc99 100644 (file)
@@ -399,11 +399,6 @@ void VG_(perform_assumed_nonblocking_syscall) ( ThreadId tid )
          VG_(panic)("syscall exit() not caught by the scheduler?!");
          break;
 
-      case __NR_sigaltstack:
-         VG_(unimplemented)
-            ("client signals on alternative stack (SA_ONSTACK)");
-         break;
-
       case __NR_clone:
          VG_(unimplemented)
             ("clone(): not supported by Valgrind.\n   "
@@ -2973,6 +2968,28 @@ void VG_(perform_assumed_nonblocking_syscall) ( ThreadId tid )
          debugging purposes, to make clients more deterministic. */
 #     define SIGNAL_SIMULATION 1
 
+      case __NR_sigaltstack: /* syscall 186 */
+         /* int sigaltstack(const stack_t *ss, stack_t *oss); */
+         if (VG_(clo_trace_syscalls))
+            VG_(printf)("sigaltstack ( %p, %p )\n",arg1,arg2);
+         if (arg1 != (UInt)NULL) {
+            must_be_readable( tst, "sigaltstack(ss)", 
+                              arg1, sizeof(vki_kstack_t) );
+         }
+         if (arg2 != (UInt)NULL) {
+            must_be_writable( tst, "sigaltstack(ss)", 
+                              arg1, sizeof(vki_kstack_t) );
+         }
+#        if SIGNAL_SIMULATION
+         VG_(do__NR_sigaltstack) (tid);
+         res = tst->m_eax;
+#        else
+         KERNEL_DO_SYSCALL(tid,res);
+#        endif
+         if (!VG_(is_kerror)(res) && res == 0 && arg2 != (UInt)NULL)
+            make_readable( arg2, sizeof(vki_kstack_t));
+         break;
+
       case __NR_rt_sigaction:
       case __NR_sigaction:
          /* int sigaction(int signum, struct k_sigaction *act,