[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