Author: Armin Rigo <ar...@tunes.org>
Branch: shadowstack-no-move
Changeset: r79811:75c41a62a963
Date: 2015-09-24 17:06 +0200
http://bitbucket.org/pypy/pypy/changeset/75c41a62a963/

Log:    Implement shadow stack support directly inside the stacklet code

diff --git a/rpython/translator/c/src/stacklet/stacklet.c 
b/rpython/translator/c/src/stacklet/stacklet.c
--- a/rpython/translator/c/src/stacklet/stacklet.c
+++ b/rpython/translator/c/src/stacklet/stacklet.c
@@ -34,15 +34,19 @@
 /************************************************************/
 
 struct stacklet_s {
-    /* The portion of the real stack claimed by this paused tealet. */
+    /* The portion of the real stack claimed by this paused stacklet. */
     char *stack_start;                /* the "near" end of the stack */
     char *stack_stop;                 /* the "far" end of the stack */
 
+    char *shadow_stack_start;         /* the "near" end of the shadow stack */
+    char *shadow_stack_stop;          /* the "far" end of the shadow stack */
+
     /* The amount that has been saved away so far, just after this struct.
      * There is enough allocated space for 'stack_stop - stack_start'
      * bytes.
      */
     ptrdiff_t stack_saved;            /* the amount saved */
+    ptrdiff_t shadow_stack_saved;
 
     /* Internally, some stacklets are arranged in a list, to handle lazy
      * saving of stacks: if the stacklet has a partially unsaved stack,
@@ -65,13 +69,16 @@
     struct stacklet_s *g_stack_chain_head;  /* NULL <=> running main */
     char *g_current_stack_stop;
     char *g_current_stack_marker;
+    char *g_current_shadow_stack_stop;      /* oldest, i.e. smallest address */
+    char *g_current_shadow_stack_marker;
     struct stacklet_s *g_source;
     struct stacklet_s *g_target;
+    char **g_shadow_stack_ref;
 };
 
 /***************************************************************/
 
-static void g_save(struct stacklet_s* g, char* stop
+static void g_save(struct stacklet_s* g, char* stop, char *shadow_stop
 #ifdef DEBUG_DUMP
                    , int overwrite_stack_for_debug
 #endif
@@ -93,10 +100,12 @@
                      |        |         |xxxxxxx|
       g->stack_start |        |         |_______| g+1
 
+       ("g+1" because it is immediately after the struct "*g" in memory)
      */
     ptrdiff_t sz1 = g->stack_saved;
     ptrdiff_t sz2 = stop - g->stack_start;
     assert(stop <= g->stack_stop);
+    /* i.e. sz2 <= g->stack_stop - g->stack_start */
 
     if (sz2 > sz1) {
         char *c = (char *)(g + 1);
@@ -111,6 +120,31 @@
 #endif
         g->stack_saved = sz2;
     }
+
+    /* Shadow stack: very similar code, but in the opposite order.
+       The shadow stack grows towards higher addresses.
+
+       g->shadow_stack_start                    _______  end
+                            |        |         |xxxxxxx|
+                            |        |         |xxxxxxx|
+                            |________|         |_______|
+                            |xxxxxxxx|    ==>  :       :
+                            |xxx __ stop       .........
+                            |xxxxxxxx|
+       g->shadow_stack_stop |________|
+    */
+    sz1 = g->shadow_stack_saved;
+    sz2 = g->shadow_stack_start - shadow_stop;
+    assert(shadow_stop >= g->shadow_stack_stop);
+    /* i.e. sz2 <= g->shadow_stack_start - g->shadow_stack_stop */
+
+    if (sz2 > sz1) {
+        char *end = ((char *)(g + 1)) +
+                    (g->stack_stop - g->stack_start) +
+                    (g->shadow_stack_start - g->shadow_stack_stop);
+        memcpy(end - sz2, shadow_stop, sz2 - sz1);
+        g->shadow_stack_saved = sz2;
+    }
 }
 
 /* Allocate and store in 'g_source' a new stacklet, which has the C
@@ -124,6 +158,10 @@
     struct stacklet_s *stacklet;
     ptrdiff_t stack_size = (thrd->g_current_stack_stop -
                             (char *)old_stack_pointer);
+    if (thrd->g_shadow_stack_ref != NULL) {
+        stack_size += (*thrd->g_shadow_stack_ref -
+                       thrd->g_current_shadow_stack_stop);
+    }
 
     thrd->g_source = malloc(sizeof(struct stacklet_s) + stack_size);
     if (thrd->g_source == NULL)
@@ -133,6 +171,15 @@
     stacklet->stack_start = old_stack_pointer;
     stacklet->stack_stop  = thrd->g_current_stack_stop;
     stacklet->stack_saved = 0;
+    if (thrd->g_shadow_stack_ref != NULL) {
+        stacklet->shadow_stack_start = *thrd->g_shadow_stack_ref;
+        stacklet->shadow_stack_stop  = thrd->g_current_shadow_stack_stop;
+    }
+    else {
+        stacklet->shadow_stack_start = NULL;
+        stacklet->shadow_stack_stop  = NULL;
+    }
+    stacklet->shadow_stack_saved = 0;
     stacklet->stack_prev  = thrd->g_stack_chain_head;
     stacklet->stack_thrd  = thrd;
     thrd->g_stack_chain_head = stacklet;
@@ -140,14 +187,18 @@
 }
 
 /* Save more of the C stack away, up to 'target_stop'.
+
+   Note that this is driven by the C stack; it is assumed that the
+   shadowstack cannot grow without the C stack also growing.
  */
 static void g_clear_stack(struct stacklet_s *g_target,
                           struct stacklet_thread_s *thrd)
 {
     struct stacklet_s *current = thrd->g_stack_chain_head;
     char *target_stop = g_target->stack_stop;
+    char *shadow_target_stop = g_target->shadow_stack_stop;
 
-    /* save and unlink tealets that are completely within
+    /* save and unlink stacklets that are completely within
        the area to clear. */
     while (current != NULL && current->stack_stop <= target_stop) {
         struct stacklet_s *prev = current->stack_prev;
@@ -155,7 +206,7 @@
         if (current != g_target) {
             /* don't bother saving away g_target, because
                it would be immediately restored */
-            g_save(current, current->stack_stop
+            g_save(current, current->stack_stop, current->shadow_stack_stop
 #ifdef DEBUG_DUMP
                    , 1
 #endif
@@ -166,7 +217,7 @@
 
     /* save a partial stack */
     if (current != NULL && current->stack_start < target_stop)
-        g_save(current, target_stop
+        g_save(current, target_stop, shadow_target_stop
 #ifdef DEBUG_DUMP
                , 1
 #endif
@@ -185,6 +236,8 @@
     if (g_allocate_source_stacklet(old_stack_pointer, thrd) < 0)
         return NULL;
     g_clear_stack(thrd->g_target, thrd);
+    if (thrd->g_shadow_stack_ref != NULL)
+        *thrd->g_shadow_stack_ref = thrd->g_target->shadow_stack_start;
     return thrd->g_target->stack_start;
 }
 
@@ -195,7 +248,8 @@
 {
     struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
     if (g_allocate_source_stacklet(old_stack_pointer, thrd) == 0)
-        g_save(thrd->g_source, thrd->g_current_stack_marker
+        g_save(thrd->g_source, thrd->g_current_stack_marker,
+               thrd->g_current_shadow_stack_marker
 #ifdef DEBUG_DUMP
                , 0
 #endif
@@ -210,6 +264,8 @@
     struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
     thrd->g_source = EMPTY_STACKLET_HANDLE;
     g_clear_stack(thrd->g_target, thrd);
+    if (thrd->g_shadow_stack_ref != NULL)
+        *thrd->g_shadow_stack_ref = thrd->g_target->shadow_stack_start;
     return thrd->g_target->stack_start;
 }
 
@@ -222,6 +278,7 @@
     struct stacklet_thread_s *thrd = (struct stacklet_thread_s *)rawthrd;
     struct stacklet_s *g = thrd->g_target;
     ptrdiff_t stack_saved = g->stack_saved;
+    ptrdiff_t shadow_stack_saved;
 
     assert(new_stack_pointer == g->stack_start);
 #if STACK_DIRECTION == 0
@@ -229,7 +286,20 @@
 #else
     memcpy(g->stack_start - stack_saved, g+1, stack_saved);
 #endif
+
+    shadow_stack_saved = g->shadow_stack_saved;
+    if (shadow_stack_saved > 0) {
+        char *end = ((char *)(g + 1)) +
+                    (g->stack_stop - g->stack_start) +
+                    (g->shadow_stack_start - g->shadow_stack_stop);
+        memcpy(g->shadow_stack_start - shadow_stack_saved,
+               end - shadow_stack_saved,
+               shadow_stack_saved);
+    }
+
     thrd->g_current_stack_stop = g->stack_stop;
+    thrd->g_current_shadow_stack_stop = g->shadow_stack_stop;
+
     free(g);
     return EMPTY_STACKLET_HANDLE;
 }
@@ -247,6 +317,7 @@
         /* First time it returns.  Only g_initial_save_state() has run
            and has created 'g_source'.  Call run(). */
         thrd->g_current_stack_stop = thrd->g_current_stack_marker;
+        thrd->g_current_shadow_stack_stop = 
thrd->g_current_shadow_stack_marker;
         result = run(thrd->g_source, run_arg);
 
         /* Then switch to 'result'. */
@@ -261,7 +332,7 @@
 
 /************************************************************/
 
-stacklet_thread_handle stacklet_newthread(void)
+stacklet_thread_handle stacklet_newthread_shadowstack(void **shadow_stack_ref)
 {
     struct stacklet_thread_s *thrd;
 
@@ -273,11 +344,18 @@
     }
 
     thrd = malloc(sizeof(struct stacklet_thread_s));
-    if (thrd != NULL)
+    if (thrd != NULL) {
         memset(thrd, 0, sizeof(struct stacklet_thread_s));
+        thrd->g_shadow_stack_ref = (char **)shadow_stack_ref;
+    }
     return thrd;
 }
 
+stacklet_thread_handle stacklet_newthread(void)
+{
+    return stacklet_newthread_shadowstack(NULL);
+}
+
 void stacklet_deletethread(stacklet_thread_handle thrd)
 {
     free(thrd);
@@ -292,6 +370,16 @@
         thrd->g_current_stack_stop = ((char *)&stackmarker) + 1;
 
     thrd->g_current_stack_marker = (char *)&stackmarker;
+
+    if (thrd->g_shadow_stack_ref != NULL) {
+        char *shadowstackmarker = *thrd->g_shadow_stack_ref;
+        assert(shadowstackmarker != NULL);
+        if (thrd->g_current_shadow_stack_stop == NULL ||
+                thrd->g_current_shadow_stack_stop > shadowstackmarker)
+            thrd->g_current_shadow_stack_stop = shadowstackmarker;
+        thrd->g_current_shadow_stack_marker = shadowstackmarker;
+    }
+
     _stacklet_initialstub(thrd, run, run_arg);
     return thrd->g_source;
 }
@@ -303,6 +391,14 @@
     if (thrd->g_current_stack_stop <= (char *)&stackmarker)
         thrd->g_current_stack_stop = ((char *)&stackmarker) + 1;
 
+    if (thrd->g_shadow_stack_ref != NULL) {
+        char *shadowstackmarker = *thrd->g_shadow_stack_ref;
+        assert(shadowstackmarker != NULL);
+        assert(thrd->g_current_shadow_stack_stop != NULL);
+        if (thrd->g_current_shadow_stack_stop > shadowstackmarker)
+            thrd->g_current_shadow_stack_stop = shadowstackmarker;
+    }
+
     thrd->g_target = target;
     _stacklet_switchstack(g_save_state, g_restore_state, thrd);
     return thrd->g_source;
@@ -350,3 +446,29 @@
   }
   return ptr;
 }
+
+char **_stacklet_translate_shadow_pointer(stacklet_handle context, char **ptr)
+{
+  char *p = (char *)ptr;
+  long delta;
+  if (context == NULL)
+    return ptr;
+  delta = context->shadow_stack_start - p;
+  if (((unsigned long)(delta - 1)) <
+                         ((unsigned long)context->shadow_stack_saved)) {
+      /* a pointer to a saved away word */
+      char *end = ((char *)(context + 1)) +
+                  (context->stack_stop - context->stack_start) +
+                  (context->shadow_stack_start - context->shadow_stack_stop);
+      return (char **)(end - delta);
+  }
+  if (((unsigned long)delta) > (unsigned long)(context->shadow_stack_start -
+                                               context->shadow_stack_stop)) {
+      /* out-of-stack pointer!  it's only ok if we are the main stacklet
+         and we are reading past the end, because the main stacklet's
+         stack stop is not exactly known. */
+      assert(delta >= 0);
+      assert(((long)context->stack_stop) & 1);
+  }
+  return ptr;
+}
diff --git a/rpython/translator/c/src/stacklet/stacklet.h 
b/rpython/translator/c/src/stacklet/stacklet.h
--- a/rpython/translator/c/src/stacklet/stacklet.h
+++ b/rpython/translator/c/src/stacklet/stacklet.h
@@ -19,10 +19,20 @@
 
 
 /* Multithread support.
+
+   Shadowstack support: if the rest of the program manages some
+   "shadowstack" data, which grows and shrinks in parallel with the C
+   stack, then it needs support here to save and restore parts of it
+   in parallel to the real stack.  To do that, pass in the arguments
+   called "shadow_stack_ref" a pointer to the current shadowstack
+   pointer.  It is assumed to grow towards higher addresses.  Pass NULL
+   if you don't need this.
  */
 typedef struct stacklet_thread_s *stacklet_thread_handle;
 
 RPY_EXTERN stacklet_thread_handle stacklet_newthread(void);
+RPY_EXTERN stacklet_thread_handle stacklet_newthread_shadowstack(
+                                                     void **shadow_stack_ref);
 RPY_EXTERN void stacklet_deletethread(stacklet_thread_handle thrd);
 
 
@@ -59,5 +69,7 @@
  */
 RPY_EXTERN
 char **_stacklet_translate_pointer(stacklet_handle context, char **ptr);
+RPY_EXTERN
+char **_stacklet_translate_shadow_pointer(stacklet_handle context, char **ptr);
 
 #endif /* _STACKLET_H_ */
diff --git a/rpython/translator/c/src/stacklet/tests.c 
b/rpython/translator/c/src/stacklet/tests.c
--- a/rpython/translator/c/src/stacklet/tests.c
+++ b/rpython/translator/c/src/stacklet/tests.c
@@ -625,6 +625,126 @@
   tealet_delete(g_sub2);
 }
 #endif
+
+static long my_shadowstack[10];
+static long *shadowstack_top = my_shadowstack;
+
+static void trash_more_shadowstack(void)
+{
+    *shadowstack_top++ = 42;
+    *shadowstack_top++ = 43;
+    *shadowstack_top++ = 44;
+    shadowstack_top -= 3;
+}
+
+stacklet_handle switchbackonce_callback_shadow_1(stacklet_handle h, void *arg)
+{
+    assert(arg == (void *)123);
+    assert(status == 0);
+    status = 1;
+    assert(h != EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack + 1);
+    *shadowstack_top++ = 1001;
+    *shadowstack_top++ = 1002;
+    trash_more_shadowstack();
+
+    h = stacklet_switch(h);
+    assert(status == 4);
+    assert(h != EMPTY_STACKLET_HANDLE);
+    status = 5;
+
+    assert(shadowstack_top == my_shadowstack + 3);
+    assert(my_shadowstack[1] == 1001);
+    assert(my_shadowstack[2] == 1002);
+    trash_more_shadowstack();
+    shadowstack_top -= 2;
+
+    return h;
+}
+
+stacklet_handle switchbackonce_callback_shadow_2(stacklet_handle h, void *arg)
+{
+    assert(arg == (void *)456);
+    assert(status == 2);
+    status = 3;
+    assert(h != EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack + 2);
+    *shadowstack_top++ = 2002;
+    *shadowstack_top++ = 2003;
+    trash_more_shadowstack();
+
+    h = stacklet_switch(h);
+    assert(status == 5);
+    assert(h != EMPTY_STACKLET_HANDLE);
+    status = 6;
+
+    assert(shadowstack_top == my_shadowstack + 4);
+    assert(my_shadowstack[2] == 2002);
+    assert(my_shadowstack[3] == 2003);
+    trash_more_shadowstack();
+    shadowstack_top -= 2;
+
+    return h;
+}
+
+void test_shadowstack(void)
+{
+    status = 0;
+    shadowstack_top = my_shadowstack;
+    *shadowstack_top++ = 500;
+    trash_more_shadowstack();
+
+    stacklet_handle h = stacklet_new(thrd, switchbackonce_callback_shadow_1,
+                                     (void *)123);
+    assert(h != EMPTY_STACKLET_HANDLE);
+    assert(status == 1);
+    status = 2;
+
+    assert(shadowstack_top == my_shadowstack + 1);
+    assert(my_shadowstack[0] == 500);
+    *shadowstack_top++ = 501;
+    trash_more_shadowstack();
+
+    stacklet_handle h2 = stacklet_new(thrd, switchbackonce_callback_shadow_2,
+                                      (void *)456);
+    assert(h2 != EMPTY_STACKLET_HANDLE);
+    assert(status == 3);
+    status = 4;
+
+    assert(shadowstack_top == my_shadowstack + 2);
+    assert(my_shadowstack[0] == 500);
+    assert(my_shadowstack[1] == 501);
+    *shadowstack_top++ = 502;
+    *shadowstack_top++ = 503;
+    trash_more_shadowstack();
+
+    h = stacklet_switch(h);
+    assert(status == 5);
+    assert(h == EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack + 4);
+    assert(my_shadowstack[0] == 500);
+    assert(my_shadowstack[1] == 501);
+    assert(my_shadowstack[2] == 502);
+    assert(my_shadowstack[3] == 503);
+    *shadowstack_top++ = 504;
+    trash_more_shadowstack();
+
+    h2 = stacklet_switch(h2);
+    assert(status == 6);
+    assert(h2 == EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack + 5);
+    assert(my_shadowstack[0] == 500);
+    assert(my_shadowstack[1] == 501);
+    assert(my_shadowstack[2] == 502);
+    assert(my_shadowstack[3] == 503);
+    assert(my_shadowstack[4] == 504);
+    shadowstack_top -= 5;
+}
+
 /************************************************************/
 
 #define TEST(name)   { name, #name }
@@ -649,6 +769,7 @@
   TEST(test_double),
   TEST(test_random),
 #endif
+  TEST(test_shadowstack),
   { NULL, NULL }
 };
 
@@ -659,7 +780,7 @@
   if (argc > 1)
     srand(atoi(argv[1]));
 
-  thrd = stacklet_newthread();
+  thrd = stacklet_newthread_shadowstack((void **)&shadowstack_top);
   for (tst=test_list; tst->runtest; tst++)
     {
       printf("+++ Running %s... +++\n", tst->name);
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to