Author: Armin Rigo <ar...@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 } }; _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit