[pypy-commit] pypy shadowstack-no-move: in-progress

arigo noreply at buildbot.pypy.org
Thu Sep 24 23:01:11 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: shadowstack-no-move
Changeset: r79815:d9733b69dd6b
Date: 2015-09-24 23:01 +0200
http://bitbucket.org/pypy/pypy/changeset/d9733b69dd6b/

Log:	in-progress

diff --git a/rpython/rlib/_rffi_stacklet.py b/rpython/rlib/_rffi_stacklet.py
--- a/rpython/rlib/_rffi_stacklet.py
+++ b/rpython/rlib/_rffi_stacklet.py
@@ -43,6 +43,8 @@
 # ----- functions -----
 
 newthread = llexternal('stacklet_newthread', [], thread_handle)
+newthread_shadowstack = llexternal('stacklet_newthread_shadowstack',
+                                   [rffi.VOIDPP], thread_handle)
 deletethread = llexternal('stacklet_deletethread',[thread_handle], lltype.Void)
 
 new = llexternal('stacklet_new', [thread_handle, run_fn, llmemory.Address],
@@ -54,3 +56,7 @@
 _translate_pointer = llexternal("_stacklet_translate_pointer",
                                 [llmemory.Address, llmemory.Address],
                                 llmemory.Address)
+get_saved_shadow = llexternal("_stacklet_get_saved_shadow",
+                              [handle], llmemory.Address)
+get_saved_shadow_size = llexternal("_stacklet_get_saved_shadow_size",
+                                   [handle], lltype.Signed)
diff --git a/rpython/rlib/_stacklet_asmgcc.py b/rpython/rlib/_stacklet_asmgcc.py
--- a/rpython/rlib/_stacklet_asmgcc.py
+++ b/rpython/rlib/_stacklet_asmgcc.py
@@ -251,6 +251,10 @@
 class StackletGcRootFinder(object):
     suspstack = NULL_SUSPSTACK
 
+    @staticmethod
+    def newthread():
+        return _c.newthread()
+
     def new(self, thrd, callback, arg):
         self.newthrd = thrd._thrd
         self.runfn = callback
diff --git a/rpython/rlib/_stacklet_shadowstack.py b/rpython/rlib/_stacklet_shadowstack.py
--- a/rpython/rlib/_stacklet_shadowstack.py
+++ b/rpython/rlib/_stacklet_shadowstack.py
@@ -1,104 +1,124 @@
 from rpython.rlib import _rffi_stacklet as _c
+from rpython.rlib import rgc
 from rpython.rlib.debug import ll_assert
 from rpython.rtyper.annlowlevel import llhelper
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
 
 
-NULL_SUSPSTACK = lltype.nullptr(llmemory.GCREF.TO)
+#
+# A GC wrapper around the C stacklet handles
+#
+STACKLET = lltype.GcStruct('Stacklet',
+                           ('handle', _c.handle),
+                           rtti=True)
+STACKLET_PTR = lltype.Ptr(STACKLET)
+NULL_STACKLET = lltype.nullptr(STACKLET)
 
+sizeofaddr = llmemory.sizeof(llmemory.Address)
+
+
+def complete_destrptr(gctransformer):
+    translator = gctransformer.translator
+    mixlevelannotator = MixLevelHelperAnnotator(translator.rtyper)
+    args_s = [lltype_to_annotation(STACKLET_PTR)]
+    s_result = annmodel.s_None
+    destrptr = mixlevelannotator.delayedfunction(stacklet_destructor,
+                                                 args_s, s_result)
+    mixlevelannotator.finish()
+    lltype.attachRuntimeTypeInfo(STACKLET, destrptr=destrptr)
+
+def customtrace(gc, obj, callback, arg):
+    stacklet = llmemory.cast_adr_to_ptr(obj, STACKLET_PTR)
+    h = stacklet.handle
+    if h:
+        addr = _c.get_saved_shadow(h)
+        stop = addr + _c.get_saved_shadow_size(h)
+        while addr != stop:
+            gc._trace_callback(callback, arg, addr)
+            addr += sizeofaddr
+lambda_customtrace = lambda: customtrace
+
+def stacklet_destructor(stacklet):
+    h = stacklet.handle
+    if h:
+        _c.destroy(h)
+
+
+def alloc_stacklet():
+    new_stacklet = lltype.malloc(STACKLET)
+    new_stacklet.handle = _c.null_handle
+    return new_stacklet
+
+def attach_handle_on_stacklet(stacklet, h):
+    if not h:
+        raise MemoryError
+    elif _c.is_empty_handle(h):
+        return NULL_STACKLET
+    else:
+        # This is a return that gave us a real handle.  Store it.
+        stacklet.handle = h
+        llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(stacklet))
+        return stacklet
+
+def consume_stacklet(stacklet):
+    h = stacklet.handle
+    ll_assert(bool(h), "consume_stacklet: null handle")
+    stacklet.handle = _c.null_handle
+    return h
 
 def _new_callback(h, arg):
-    # We still have the old shadowstack active at this point; save it
-    # away, and start a fresh new one
-    oldsuspstack = gcrootfinder.oldsuspstack
-    h = llmemory.cast_ptr_to_adr(h)
-    llop.gc_save_current_state_away(lltype.Void,
-                                    oldsuspstack, h)
-    llop.gc_start_fresh_new_state(lltype.Void)
-    gcrootfinder.oldsuspstack = NULL_SUSPSTACK
+    # There is a fresh stacklet object waiting on the gcrootfinder,
+    # so populate it with data that represents the parent suspended
+    # stacklet and detach the stacklet object from gcrootfinder.
+    stacklet = gcrootfinder.fresh_stacklet
+    gcrootfinder.fresh_stacklet = NULL_STACKLET
+    stacklet = attach_handle_on_stacklet(stacklet, h)
+    assert stacklet
     #
-    newsuspstack = gcrootfinder.callback(oldsuspstack, arg)
+    # Call the main function provided by the (RPython) user.
+    stacklet = gcrootfinder.runfn(stacklet, arg)
     #
-    # Finishing this stacklet.
-    gcrootfinder.oldsuspstack = NULL_SUSPSTACK
-    gcrootfinder.newsuspstack = newsuspstack
-    h = llop.gc_shadowstackref_context(llmemory.Address, newsuspstack)
-    return llmemory.cast_adr_to_ptr(h, _c.handle)
-
-def prepare_old_suspstack():
-    if not gcrootfinder.oldsuspstack:   # else reuse the one still there
-        _allocate_old_suspstack()
-
-def _allocate_old_suspstack():
-    suspstack = llop.gc_shadowstackref_new(llmemory.GCREF)
-    gcrootfinder.oldsuspstack = suspstack
-_allocate_old_suspstack._dont_inline_ = True
-
-def get_result_suspstack(h):
-    # Now we are in the target, after the switch() or the new().
-    # Note that this whole module was carefully written in such a way as
-    # not to invoke pushing/popping things off the shadowstack at
-    # unexpected moments...
-    oldsuspstack = gcrootfinder.oldsuspstack
-    newsuspstack = gcrootfinder.newsuspstack
-    gcrootfinder.oldsuspstack = NULL_SUSPSTACK
-    gcrootfinder.newsuspstack = NULL_SUSPSTACK
-    if not h:
-        raise MemoryError
-    # We still have the old shadowstack active at this point; save it
-    # away, and restore the new one
-    if oldsuspstack:
-        ll_assert(not _c.is_empty_handle(h),"unexpected empty stacklet handle")
-        h = llmemory.cast_ptr_to_adr(h)
-        llop.gc_save_current_state_away(lltype.Void, oldsuspstack, h)
-    else:
-        ll_assert(_c.is_empty_handle(h),"unexpected non-empty stacklet handle")
-        llop.gc_forget_current_state(lltype.Void)
-    #
-    llop.gc_restore_state_from(lltype.Void, newsuspstack)
-    #
-    # From this point on, 'newsuspstack' is consumed and done, its
-    # shadow stack installed as the current one.  It should not be
-    # used any more.  For performance, we avoid it being deallocated
-    # by letting it be reused on the next switch.
-    gcrootfinder.oldsuspstack = newsuspstack
-    # Return.
-    return oldsuspstack
+    # Here, 'stacklet' points to the target stacklet to which we want
+    # to jump to next.  Read the 'handle' and forget about the
+    # stacklet object.
+    return consume_stacklet(stacklet)
 
 
 class StackletGcRootFinder(object):
+    fresh_stacklet = NULL_STACKLET
+
+    @staticmethod
+    def newthread():
+        rst_addr = llop.gc_adr_of_root_stack_top(llmemory.Address)
+        return _c.newthread_shadowstack(rffi.cast(rffi.VOIDPP, rst_addr))
+
     def new(thrd, callback, arg):
-        gcrootfinder.callback = callback
+        rgc.register_custom_trace_hook(STACKLET, lambda_customtrace)
+        result_stacklet = alloc_stacklet()
+        gcrootfinder.fresh_stacklet = alloc_stacklet()
+        gcrootfinder.runfn = callback
         thread_handle = thrd._thrd
-        prepare_old_suspstack()
         h = _c.new(thread_handle, llhelper(_c.run_fn, _new_callback), arg)
-        return get_result_suspstack(h)
+        return attach_handle_on_stacklet(result_stacklet, h)
     new._dont_inline_ = True
     new = staticmethod(new)
 
-    def switch(suspstack):
-        # suspstack has a handle to target, i.e. where to switch to
-        ll_assert(suspstack != gcrootfinder.oldsuspstack,
-                  "stacklet: invalid use")
-        gcrootfinder.newsuspstack = suspstack
-        h = llop.gc_shadowstackref_context(llmemory.Address, suspstack)
-        h = llmemory.cast_adr_to_ptr(h, _c.handle)
-        prepare_old_suspstack()
+    def switch(stacklet):
+        # 'stacklet' has a handle to target, i.e. where to switch to
+        h = consume_stacklet(stacklet)
         h = _c.switch(h)
-        return get_result_suspstack(h)
+        return attach_handle_on_stacklet(stacklet, h)
     switch._dont_inline_ = True
     switch = staticmethod(switch)
 
     @staticmethod
-    def is_empty_handle(suspstack):
-        return not suspstack
+    def is_empty_handle(stacklet):
+        return not stacklet
 
     @staticmethod
     def get_null_handle():
-        return NULL_SUSPSTACK
+        return NULL_STACKLET
 
 
 gcrootfinder = StackletGcRootFinder()
-gcrootfinder.oldsuspstack = NULL_SUSPSTACK
-gcrootfinder.newsuspstack = NULL_SUSPSTACK
diff --git a/rpython/rlib/rstacklet.py b/rpython/rlib/rstacklet.py
--- a/rpython/rlib/rstacklet.py
+++ b/rpython/rlib/rstacklet.py
@@ -12,7 +12,7 @@
     @jit.dont_look_inside
     def __init__(self, config):
         self._gcrootfinder = _getgcrootfinder(config, we_are_translated())
-        self._thrd = _c.newthread()
+        self._thrd = self._gcrootfinder.newthread()
         if not self._thrd:
             raise MemoryError
         self._thrd_deleter = StackletThreadDeleter(self._thrd)
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
@@ -226,6 +226,16 @@
     thrd->g_stack_chain_head = current;
 }
 
+static void move_shadow_stack_ref(struct stacklet_thread_s *thrd, char *target)
+{
+    if (thrd->g_shadow_stack_ref != NULL) {
+        char *source = *thrd->g_shadow_stack_ref;
+        if (target > source)
+            memset(source, 0, target - source);
+        *thrd->g_shadow_stack_ref = target;
+    }
+}
+
 /* This saves the current state in a new stacklet that gets stored in
  * 'g_source', and save away enough of the stack to allow a jump to
  * 'g_target'.
@@ -236,8 +246,7 @@
     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;
+    move_shadow_stack_ref(thrd, thrd->g_target->shadow_stack_start);
     return thrd->g_target->stack_start;
 }
 
@@ -264,8 +273,7 @@
     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;
+    move_shadow_stack_ref(thrd, thrd->g_target->shadow_stack_start);
     return thrd->g_target->stack_start;
 }
 
@@ -447,28 +455,15 @@
   return ptr;
 }
 
-char **_stacklet_translate_shadow_pointer(stacklet_handle context, char **ptr)
+char *_stacklet_get_saved_shadow(stacklet_handle context)
 {
-  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;
+    char *end = ((char *)(context + 1)) +
+                (context->stack_stop - context->stack_start) +
+                (context->shadow_stack_start - context->shadow_stack_stop);
+    return end - context->shadow_stack_saved;
 }
+
+long _stacklet_get_saved_shadow_size(stacklet_handle context)
+{
+    return context->shadow_stack_saved;
+}
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
@@ -69,7 +69,10 @@
  */
 RPY_EXTERN
 char **_stacklet_translate_pointer(stacklet_handle context, char **ptr);
-RPY_EXTERN
-char **_stacklet_translate_shadow_pointer(stacklet_handle context, char **ptr);
+
+/* Hack for shadowstack: fish the portion of shadowstack that is stored
+   in some stacklet_handle */
+RPY_EXTERN char *_stacklet_get_saved_shadow(stacklet_handle context);
+RPY_EXTERN long _stacklet_get_saved_shadow_size(stacklet_handle context);
 
 #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
@@ -655,6 +655,7 @@
     status = 5;
 
     assert(shadowstack_top == my_shadowstack + 3);
+    assert(my_shadowstack[0] != 9999);
     assert(my_shadowstack[1] == 1001);
     assert(my_shadowstack[2] == 1002);
     trash_more_shadowstack();
@@ -681,6 +682,8 @@
     status = 6;
 
     assert(shadowstack_top == my_shadowstack + 4);
+    assert(my_shadowstack[0] != 9999);
+    assert(my_shadowstack[1] != 9999);
     assert(my_shadowstack[2] == 2002);
     assert(my_shadowstack[3] == 2003);
     trash_more_shadowstack();
@@ -745,6 +748,56 @@
     shadowstack_top -= 5;
 }
 
+void test_shadowstack_clear(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 -= 2;
+    my_shadowstack[0] = 9999;
+    my_shadowstack[1] = 9999;
+    my_shadowstack[2] = 9999;
+    my_shadowstack[3] = 9999;
+
+    h = stacklet_switch(h);
+    assert(status == 5);
+    assert(h == EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack);
+    my_shadowstack[0] = 9999;
+    my_shadowstack[1] = 9999;
+    my_shadowstack[2] = 9999;
+    my_shadowstack[3] = 9999;
+
+    h2 = stacklet_switch(h2);
+    assert(status == 6);
+    assert(h2 == EMPTY_STACKLET_HANDLE);
+
+    assert(shadowstack_top == my_shadowstack);
+}
+
 /************************************************************/
 
 #define TEST(name)   { name, #name }
@@ -770,6 +823,7 @@
   TEST(test_random),
 #endif
   TEST(test_shadowstack),
+  TEST(test_shadowstack_clear),
   { NULL, NULL }
 };
 


More information about the pypy-commit mailing list