[pypy-commit] pypy stacklet: Shadowstack support, version 1.
arigo
noreply at buildbot.pypy.org
Mon Aug 8 17:08:47 CEST 2011
Author: Armin Rigo <arigo at tunes.org>
Branch: stacklet
Changeset: r46372:d2364a7ee11b
Date: 2011-08-08 17:07 +0200
http://bitbucket.org/pypy/pypy/changeset/d2364a7ee11b/
Log: Shadowstack support, version 1.
diff --git a/pypy/config/translationoption.py b/pypy/config/translationoption.py
--- a/pypy/config/translationoption.py
+++ b/pypy/config/translationoption.py
@@ -26,8 +26,7 @@
"translation", "Translation Options", [
BoolOption("stacklet", "enable stackless features during compilation",
default=False, cmdline="--stacklet",
- requires=[("translation.type_system", "lltype"),
- ("translation.gcrootfinder", "asmgcc")]), # XXX temp
+ requires=[("translation.type_system", "lltype")]),
ChoiceOption("type_system", "Type system to use when RTyping",
["lltype", "ootype"], cmdline=None, default="lltype",
requires={
diff --git a/pypy/rlib/_stacklet_asmgcc.py b/pypy/rlib/_stacklet_asmgcc.py
--- a/pypy/rlib/_stacklet_asmgcc.py
+++ b/pypy/rlib/_stacklet_asmgcc.py
@@ -223,7 +223,7 @@
suspstack = NULL_SUSPSTACK
def new(self, thrd, callback, arg):
- self.thrd = thrd
+ self.thrd = thrd._thrd
self.runfn = callback
self.arg = arg
# make a fresh new clean SUSPSTACK
@@ -236,7 +236,7 @@
return self.get_result_suspstack(h)
def switch(self, thrd, suspstack):
- self.thrd = thrd
+ self.thrd = thrd._thrd
self.suspstack = suspstack
h = pypy_asm_stackwalk2(llhelper(FUNCNOARG_P, _switch_callback),
alternateanchor)
@@ -264,16 +264,14 @@
def destroy(self, thrd, suspstack):
h = suspstack.handle
- a = suspstack.anchor
suspstack.handle = _c.null_handle
- _c.destroy(thrd, h)
- lltype.free(a, flavor='raw')
+ _c.destroy(thrd._thrd, h)
def is_empty_handle(self, suspstack):
return not suspstack
def get_null_handle(self):
- return lltype.nullptr(SUSPSTACK)
+ return NULL_SUSPSTACK
gcrootfinder = StackletGcRootFinder()
diff --git a/pypy/rlib/_stacklet_n_a.py b/pypy/rlib/_stacklet_n_a.py
--- a/pypy/rlib/_stacklet_n_a.py
+++ b/pypy/rlib/_stacklet_n_a.py
@@ -7,19 +7,20 @@
__metaclass__ = StaticMethods
def new(thrd, callback, arg):
- h = _c.new(thrd, llhelper(_c.run_fn, callback), arg)
+ h = _c.new(thrd._thrd, llhelper(_c.run_fn, callback), arg)
if not h:
raise MemoryError
return h
new._annspecialcase_ = 'specialize:arg(1)'
def switch(thrd, h):
- h = _c.switch(thrd, h)
+ h = _c.switch(thrd._thrd, h)
if not h:
raise MemoryError
return h
- destroy = _c.destroy
+ def destroy(thrd, h):
+ _c.destroy(thrd._thrd, h)
is_empty_handle = _c.is_empty_handle
diff --git a/pypy/rlib/_stacklet_shadowstack.py b/pypy/rlib/_stacklet_shadowstack.py
new file mode 100644
--- /dev/null
+++ b/pypy/rlib/_stacklet_shadowstack.py
@@ -0,0 +1,196 @@
+from pypy.rlib import _rffi_stacklet as _c
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
+from pypy.rpython.lltypesystem.lloperation import llop
+from pypy.rlib import rstacklet
+from pypy.rlib.debug import ll_assert
+from pypy.rpython.annlowlevel import llhelper
+
+
+PTR_SUSPSTACK = lltype.Ptr(lltype.GcForwardReference())
+SUSPSTACK = lltype.GcStruct('SuspStack',
+ ('handle', _c.handle),
+ ('shadowstack_stop', llmemory.Address), # low (old)
+ ('shadowstack_start', llmemory.Address),# high(new)
+ ('shadowstack_saved', lltype.Signed), # number
+ ('shadowstack_prev', PTR_SUSPSTACK),
+ # copy of the shadowstack, but in reversed order:
+ # the first items here are the last items in the
+ # real shadowstack. Only items 0 to saved-1 are
+ # set up; the rest is still in the real shadowstack
+ ('shadowstack_copy', lltype.Array(llmemory.GCREF)))
+PTR_SUSPSTACK.TO.become(SUSPSTACK)
+NULL_SUSPSTACK = lltype.nullptr(SUSPSTACK)
+
+sizeofaddr = llmemory.sizeof(llmemory.Address)
+
+def repr_suspstack(suspstack):
+ return '<SuspStack %d: stop=%d, start=%d, saved=%d>' % (
+ rffi.cast(lltype.Signed, suspstack.handle),
+ rffi.cast(lltype.Signed, suspstack.shadowstack_stop),
+ rffi.cast(lltype.Signed, suspstack.shadowstack_start),
+ suspstack.shadowstack_saved)
+
+
+# every thread has a 'current stack stop', which is like
+# g_current_stack_stop in stacklet.c but about the shadowstack
+rstacklet.StackletThread._current_shadowstack_stop = llmemory.NULL
+
+# every thread has a 'stack chain' too, similar to g_stack_chain_head.
+rstacklet.StackletThread._shadowstack_chain_head = NULL_SUSPSTACK
+
+
+def root_stack_top():
+ return llop.gc_adr_of_root_stack_top(llmemory.Address).address[0]
+
+def set_root_stack_top(newaddr):
+ llop.gc_adr_of_root_stack_top(llmemory.Address).address[0] = newaddr
+
+
+def _new_runfn(h, arg):
+ gcrootfinder.thrd._current_shadowstack_stop = root_stack_top()
+ suspstack = gcrootfinder.attach_handle_on_suspstack(h)
+ suspstack = gcrootfinder.runfn(suspstack, arg)
+ return gcrootfinder.consume_suspstack(suspstack)
+
+
+class StackletGcRootFinder(object):
+ suspstack = NULL_SUSPSTACK
+
+ def new(self, thrd, callback, arg):
+ if thrd._current_shadowstack_stop == llmemory.NULL:
+ thrd._current_shadowstack_stop = root_stack_top()
+ self.allocate_source_suspstack(thrd)
+ self.runfn = callback
+ h = _c.new(thrd._thrd, llhelper(_c.run_fn, _new_runfn), arg)
+ return self.get_result_suspstack(h)
+
+ def switch(self, thrd, suspstack):
+ self.allocate_source_suspstack(thrd)
+ h = self.consume_suspstack(suspstack)
+ h2 = _c.switch(thrd._thrd, h)
+ return self.get_result_suspstack(h2)
+
+ def allocate_source_suspstack(self, thrd):
+ # Attach to 'self.suspstack' a SUSPSTACK that represents the
+ # old, but still current, stacklet. All that is left to
+ # fill is 'self.suspstack.handle', done later by a call
+ # to attach_handle_on_suspstack().
+ rst = root_stack_top()
+ if thrd._current_shadowstack_stop > rst:
+ thrd._current_shadowstack_stop = rst
+ count = (rst - thrd._current_shadowstack_stop) // sizeofaddr
+ newsuspstack = lltype.malloc(SUSPSTACK, count)
+ newsuspstack.shadowstack_stop = thrd._current_shadowstack_stop
+ newsuspstack.shadowstack_start = rst
+ newsuspstack.shadowstack_saved = 0
+ newsuspstack.shadowstack_prev = thrd._shadowstack_chain_head
+ thrd._shadowstack_chain_head = newsuspstack
+ self.suspstack = newsuspstack
+ self.thrd = thrd
+
+ def attach_handle_on_suspstack(self, handle):
+ s = self.suspstack
+ self.suspstack = NULL_SUSPSTACK
+ s.handle = handle
+ print 'ATTACH HANDLE', repr_suspstack(s)
+ return s
+
+ def get_result_suspstack(self, h):
+ #
+ # Return from a new() or a switch(): 'h' is a handle, possibly
+ # an empty one, that says from where we switched to.
+ if not h:
+ # oups, we didn't actually switch anywhere, but just got
+ # an out-of-memory condition. Restore the current suspstack.
+ self.consume_suspstack(self.suspstack)
+ self.suspstack = NULL_SUSPSTACK
+ self.thrd = None
+ raise MemoryError
+ #
+ if _c.is_empty_handle(h):
+ self.thrd = None
+ return NULL_SUSPSTACK
+ else:
+ # This is a return that gave us a real handle. Store it.
+ return self.attach_handle_on_suspstack(h)
+
+ def consume_suspstack(self, suspstack):
+ print 'CONSUME', repr_suspstack(suspstack)
+ #
+ # We want to switch to or return to 'suspstack'. First get
+ # how far we have to clean up the shadowstack.
+ target = suspstack.shadowstack_stop
+ #
+ # Clean the shadowstack up to that position.
+ self.clear_shadowstack(target, suspstack)
+ #
+ # Now restore data from suspstack.shadowstack_copy.
+ self.restore_suspstack(suspstack)
+ #
+ # Set the new root stack bounds.
+ self.thrd._current_shadowstack_stop = target
+ set_root_stack_top(suspstack.shadowstack_start)
+ #
+ # Now the real shadowstack is ready for 'suspstack'.
+ return suspstack.handle
+
+ def clear_shadowstack(self, target_stop, targetsuspstack):
+ # NB. see also g_clear_stack() in stacklet.c.
+ #
+ current = self.thrd._shadowstack_chain_head
+ #
+ # save and unlink suspstacks that are completely within
+ # the area to clear.
+ while bool(current) and current.shadowstack_stop >= target_stop:
+ prev = current.shadowstack_prev
+ current.shadowstack_prev = NULL_SUSPSTACK
+ if current != targetsuspstack:
+ # don't bother saving away targetsuspstack, because
+ # it would be immediately restored
+ self._save(current, current.shadowstack_stop)
+ current = prev
+ #
+ # save a partial stack
+ if bool(current) and current.shadowstack_start > target_stop:
+ self._save(current, target_stop)
+ #
+ self.thrd._shadowstack_chain_head = current
+
+ def _save(self, suspstack, stop):
+ # See g_save() in stacklet.c.
+ num1 = suspstack.shadowstack_saved
+ num2 = (suspstack.shadowstack_start - stop) // sizeofaddr
+ ll_assert(stop >= suspstack.shadowstack_stop, "stacklet+shadowstack#1")
+ source = suspstack.shadowstack_start - num1 * sizeofaddr
+ while num1 < num2:
+ source -= sizeofaddr
+ addr = source.address[0]
+ gcref = llmemory.cast_adr_to_ptr(addr, llmemory.GCREF)
+ suspstack.shadowstack_copy[num1] = gcref
+ num1 += 1
+ suspstack.shadowstack_saved = num1
+
+ def restore_suspstack(self, suspstack):
+ target = suspstack.shadowstack_start
+ saved = suspstack.shadowstack_saved
+ suspstack.shadowstack_saved = 0
+ i = 0
+ while i < saved:
+ addr = llmemory.cast_ptr_to_adr(suspstack.shadowstack_copy[i])
+ target -= sizeofaddr
+ target.address[0] = addr
+ i += 1
+
+ def destroy(self, thrd, suspstack):
+ h = suspstack.handle
+ suspstack.handle = _c.null_handle
+ _c.destroy(thrd._thrd, h)
+
+ def is_empty_handle(self, suspstack):
+ return not suspstack
+
+ def get_null_handle(self):
+ return NULL_SUSPSTACK
+
+
+gcrootfinder = StackletGcRootFinder()
diff --git a/pypy/rlib/rstacklet.py b/pypy/rlib/rstacklet.py
--- a/pypy/rlib/rstacklet.py
+++ b/pypy/rlib/rstacklet.py
@@ -17,14 +17,14 @@
_c.deletethread(thrd)
def new(self, callback, arg=llmemory.NULL):
- return self._gcrootfinder.new(self._thrd, callback, arg)
+ return self._gcrootfinder.new(self, callback, arg)
new._annspecialcase_ = 'specialize:arg(1)'
def switch(self, stacklet):
- return self._gcrootfinder.switch(self._thrd, stacklet)
+ return self._gcrootfinder.switch(self, stacklet)
def destroy(self, stacklet):
- self._gcrootfinder.destroy(self._thrd, stacklet)
+ self._gcrootfinder.destroy(self, stacklet)
def is_empty_handle(self, stacklet):
return self._gcrootfinder.is_empty_handle(stacklet)
diff --git a/pypy/rlib/test/test_rstacklet.py b/pypy/rlib/test/test_rstacklet.py
--- a/pypy/rlib/test/test_rstacklet.py
+++ b/pypy/rlib/test/test_rstacklet.py
@@ -1,7 +1,7 @@
import gc
from pypy.rlib import rstacklet, rrandom
from pypy.rlib.rarithmetic import intmask
-from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.lltypesystem import lltype, llmemory, rffi
from pypy.translator.c.test.test_standalone import StandaloneTests
@@ -26,7 +26,7 @@
@here_is_a_test
def test_new(self):
print 'start'
- h = self.sthread.new(empty_callback, rffi.cast(rffi.VOIDP, 123))
+ h = self.sthread.new(empty_callback, rffi.cast(llmemory.Address, 123))
print 'end', h
assert self.sthread.is_empty_handle(h)
@@ -40,7 +40,7 @@
def test_simple_switch(self):
self.status = 0
h = self.sthread.new(switchbackonce_callback,
- rffi.cast(rffi.VOIDP, 321))
+ rffi.cast(llmemory.Address, 321))
assert not self.sthread.is_empty_handle(h)
self.nextstatus(2)
h = self.sthread.switch(h)
@@ -110,7 +110,7 @@
# start a new stacklet
print "NEW", n
h = runner.sthread.new(variousstackdepths_callback,
- rffi.cast(rffi.VOIDP, n))
+ rffi.cast(llmemory.Address, n))
else:
# switch to this stacklet
print "switch to", n
@@ -251,6 +251,10 @@
gc = 'minimark'
gcrootfinder = 'asmgcc'
+class TestStackletShadowStack(BaseTestStacklet):
+ gc = 'minimark'
+ gcrootfinder = 'shadowstack'
+
def target(*args):
return entry_point, None
diff --git a/pypy/rpython/memory/gctransform/shadowstack.py b/pypy/rpython/memory/gctransform/shadowstack.py
--- a/pypy/rpython/memory/gctransform/shadowstack.py
+++ b/pypy/rpython/memory/gctransform/shadowstack.py
@@ -64,6 +64,9 @@
if self.collect_stacks_from_other_threads is not None:
self.collect_stacks_from_other_threads(collect_stack_root)
+ def need_stacklet_support(self):
+ pass # no special code needed here
+
def need_thread_support(self, gctransformer, getfn):
from pypy.module.thread import ll_thread # xxx fish
from pypy.rpython.memory.support import AddressDict
More information about the pypy-commit
mailing list