[pypy-commit] stmgc default: re-add shadowstack

Raemi noreply at buildbot.pypy.org
Wed Sep 3 15:44:40 CEST 2014


Author: Remi Meier <remi.meier at inf.ethz.ch>
Branch: 
Changeset: r1341:4a48705ca8ce
Date: 2014-09-03 15:45 +0200
http://bitbucket.org/pypy/stmgc/changeset/4a48705ca8ce/

Log:	re-add shadowstack

diff --git a/c8/stm/core.c b/c8/stm/core.c
--- a/c8/stm/core.c
+++ b/c8/stm/core.c
@@ -130,6 +130,7 @@
 #ifndef NDEBUG
     STM_PSEGMENT->running_pthread = pthread_self();
 #endif
+    STM_PSEGMENT->shadowstack_at_start_of_transaction = tl->shadowstack;
 
     dprintf(("start_transaction\n"));
 
@@ -260,11 +261,30 @@
 #pragma push_macro("STM_SEGMENT")
 #undef STM_PSEGMENT
 #undef STM_SEGMENT
-    /* struct stm_priv_segment_info_s *pseg = get_priv_segment(segment_num); */
+    struct stm_priv_segment_info_s *pseg = get_priv_segment(segment_num);
 
-    /* throw_away_nursery(pseg); */
+    throw_away_nursery(pseg);
 
-    /* reset_modified_from_other_segments(segment_num); */
+    /* XXX: reset_modified_from_other_segments(segment_num); */
+
+    stm_thread_local_t *tl = pseg->pub.running_thread;
+#ifdef STM_NO_AUTOMATIC_SETJMP
+    /* In tests, we don't save and restore the shadowstack correctly.
+       Be sure to not change items below shadowstack_at_start_of_transaction.
+       There is no such restrictions in non-Python-based tests. */
+    assert(tl->shadowstack >= pseg->shadowstack_at_start_of_transaction);
+    tl->shadowstack = pseg->shadowstack_at_start_of_transaction;
+#else
+    /* NB. careful, this function might be called more than once to
+       abort a given segment.  Make sure that
+       stm_rewind_jmp_restore_shadowstack() is idempotent. */
+    /* we need to do this here and not directly in rewind_longjmp() because
+       that is called when we already released everything (safe point)
+       and a concurrent major GC could mess things up. */
+    if (tl->shadowstack != NULL)
+        stm_rewind_jmp_restore_shadowstack(tl);
+    assert(tl->shadowstack == pseg->shadowstack_at_start_of_transaction);
+#endif
 
 #pragma pop_macro("STM_SEGMENT")
 #pragma pop_macro("STM_PSEGMENT")
diff --git a/c8/stm/core.h b/c8/stm/core.h
--- a/c8/stm/core.h
+++ b/c8/stm/core.h
@@ -54,6 +54,8 @@
 
     struct stm_commit_log_entry_s *last_commit_log_entry;
 
+    struct stm_shadowentry_s *shadowstack_at_start_of_transaction;
+
     /* For debugging */
 #ifndef NDEBUG
     pthread_t running_pthread;
diff --git a/c8/stm/nursery.c b/c8/stm/nursery.c
--- a/c8/stm/nursery.c
+++ b/c8/stm/nursery.c
@@ -87,6 +87,33 @@
 }
 
 
+static void collect_roots_in_nursery(void)
+{
+    stm_thread_local_t *tl = STM_SEGMENT->running_thread;
+    struct stm_shadowentry_s *current = tl->shadowstack;
+    struct stm_shadowentry_s *finalbase = tl->shadowstack_base;
+    struct stm_shadowentry_s *ssbase;
+    ssbase = (struct stm_shadowentry_s *)tl->rjthread.moved_off_ssbase;
+    if (ssbase == NULL)
+        ssbase = finalbase;
+    else
+        assert(finalbase <= ssbase && ssbase <= current);
+
+    while (current > ssbase) {
+        --current;
+        uintptr_t x = (uintptr_t)current->ss;
+
+        if ((x & 3) == 0) {
+            /* the stack entry is a regular pointer (possibly NULL) */
+            minor_trace_if_young(&current->ss);
+        }
+        else {
+            /* it is an odd-valued marker, ignore */
+        }
+    }
+}
+
+
 static inline void _collect_now(object_t *obj)
 {
     assert(!_is_young(obj));
@@ -159,6 +186,8 @@
 {
     dprintf(("minor_collection commit=%d\n", (int)commit));
 
+    collect_roots_in_nursery();
+
     collect_oldrefs_to_nursery();
 
     assert(list_is_empty(STM_PSEGMENT->objects_pointing_to_nursery));
diff --git a/c8/stm/setup.c b/c8/stm/setup.c
--- a/c8/stm/setup.c
+++ b/c8/stm/setup.c
@@ -149,6 +149,45 @@
     teardown_pages();
 }
 
+static void _shadowstack_trap_page(char *start, int prot)
+{
+    size_t bsize = STM_SHADOW_STACK_DEPTH * sizeof(struct stm_shadowentry_s);
+    char *end = start + bsize + 4095;
+    end -= (((uintptr_t)end) & 4095);
+    mprotect(end, 4096, prot);
+}
+
+static void _init_shadow_stack(stm_thread_local_t *tl)
+{
+    size_t bsize = STM_SHADOW_STACK_DEPTH * sizeof(struct stm_shadowentry_s);
+    char *start = malloc(bsize + 8192);  /* for the trap page, plus rounding */
+    if (!start)
+        stm_fatalerror("can't allocate shadow stack");
+
+    /* set up a trap page: if the shadowstack overflows, it will
+       crash in a clean segfault */
+    _shadowstack_trap_page(start, PROT_NONE);
+
+    struct stm_shadowentry_s *s = (struct stm_shadowentry_s *)start;
+    tl->shadowstack = s;
+    tl->shadowstack_base = s;
+    STM_PUSH_ROOT(*tl, -1);
+}
+
+static void _done_shadow_stack(stm_thread_local_t *tl)
+{
+    assert(tl->shadowstack > tl->shadowstack_base);
+    assert(tl->shadowstack_base->ss == (object_t *)-1);
+
+    char *start = (char *)tl->shadowstack_base;
+    _shadowstack_trap_page(start, PROT_READ | PROT_WRITE);
+
+    free(tl->shadowstack_base);
+    tl->shadowstack = NULL;
+    tl->shadowstack_base = NULL;
+}
+
+
 static pthread_t *_get_cpth(stm_thread_local_t *tl)
 {
     assert(sizeof(pthread_t) <= sizeof(tl->creating_pthread));
@@ -177,6 +216,7 @@
     num = (num + 1) % NB_SEGMENTS;
     tl->associated_segment_num = num;
     *_get_cpth(tl) = pthread_self();
+    _init_shadow_stack(tl);
     set_gs_register(get_segment_base(num));
     s_mutex_unlock();
 }
@@ -186,7 +226,7 @@
     s_mutex_lock();
     assert(tl->prev != NULL);
     assert(tl->next != NULL);
-
+    _done_shadow_stack(tl);
     if (tl == stm_all_thread_locals) {
         stm_all_thread_locals = stm_all_thread_locals->next;
         if (tl == stm_all_thread_locals) {
diff --git a/c8/stmgc.h b/c8/stmgc.h
--- a/c8/stmgc.h
+++ b/c8/stmgc.h
@@ -48,9 +48,16 @@
 #define STM_SEGMENT           ((stm_segment_info_t *)4352)
 
 
+struct stm_shadowentry_s {
+    /* Like stm_read_marker_s, this is a struct to enable better
+       aliasing analysis in the C code. */
+    object_t *ss;
+};
+
 typedef struct stm_thread_local_s {
     /* rewind_setjmp's interface */
     rewind_jmp_thread rjthread;
+    struct stm_shadowentry_s *shadowstack, *shadowstack_base;
     /* the next fields are handled internally by the library */
     int associated_segment_num;
     struct stm_thread_local_s *prev, *next;
@@ -146,6 +153,10 @@
 void stm_setup(void);
 void stm_teardown(void);
 
+#define STM_SHADOW_STACK_DEPTH   163840
+#define STM_PUSH_ROOT(tl, p)   ((tl).shadowstack++->ss = (object_t *)(p))
+#define STM_POP_ROOT(tl, p)    ((p) = (typeof(p))((--(tl).shadowstack)->ss))
+#define STM_POP_ROOT_RET(tl)   ((--(tl).shadowstack)->ss)
 
 void stm_register_thread_local(stm_thread_local_t *tl);
 void stm_unregister_thread_local(stm_thread_local_t *tl);
@@ -162,6 +173,13 @@
     rewind_jmp_longjmp(&(tl)->rjthread)
 #define stm_rewind_jmp_forget(tl)                  \
     rewind_jmp_forget(&(tl)->rjthread)
+#define stm_rewind_jmp_restore_shadowstack(tl)  do {     \
+    assert(rewind_jmp_armed(&(tl)->rjthread));           \
+    (tl)->shadowstack = (struct stm_shadowentry_s *)     \
+        rewind_jmp_restore_shadowstack(&(tl)->rjthread); \
+} while (0)
+#define stm_rewind_jmp_enum_shadowstack(tl, callback)    \
+    rewind_jmp_enum_shadowstack(&(tl)->rjthread, callback)
 
 
 long stm_start_transaction(stm_thread_local_t *tl);
diff --git a/c8/test/support.py b/c8/test/support.py
--- a/c8/test/support.py
+++ b/c8/test/support.py
@@ -15,8 +15,14 @@
 ...;
 } rewind_jmp_thread;
 
+struct stm_shadowentry_s {
+    object_t *ss;
+};
+
+
 typedef struct {
     rewind_jmp_thread rjthread;
+    struct stm_shadowentry_s *shadowstack, *shadowstack_base;
     int associated_segment_num;
     struct stm_thread_local_s *prev, *next;
     void *creating_pthread[2];
@@ -372,10 +378,15 @@
 
 
 
+SHADOWSTACK_LENGTH = 1000
 _keepalive = weakref.WeakKeyDictionary()
 
 def _allocate_thread_local():
     tl = ffi.new("stm_thread_local_t *")
+    ss = ffi.new("struct stm_shadowentry_s[]", SHADOWSTACK_LENGTH)
+    _keepalive[tl] = ss
+    tl.shadowstack = ss
+    tl.shadowstack_base = ss
     lib.stm_register_thread_local(tl)
     return tl
 
@@ -445,10 +456,22 @@
             stm_validate() # can raise
 
     def push_root(self, o):
-        assert 0
+        assert ffi.typeof(o) == ffi.typeof("object_t *")
+        tl = self.tls[self.current_thread]
+        curlength = tl.shadowstack - tl.shadowstack_base
+        assert 0 <= curlength < SHADOWSTACK_LENGTH
+        tl.shadowstack[0].ss = ffi.cast("object_t *", o)
+        tl.shadowstack += 1
 
     def pop_root(self):
-        assert 0
+        tl = self.tls[self.current_thread]
+        curlength = tl.shadowstack - tl.shadowstack_base
+        assert curlength >= 1
+        if curlength == 1:
+            raise EmptyStack
+        assert 0 < curlength <= SHADOWSTACK_LENGTH
+        tl.shadowstack -= 1
+        return ffi.cast("object_t *", tl.shadowstack[0].ss)
 
     def push_root_no_gc(self):
         "Pushes an invalid object, to crash in case the GC is called"


More information about the pypy-commit mailing list