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