[pypy-commit] pypy default: hg merge shadowstack-issue2722
arigo
pypy.commits at gmail.com
Wed May 8 09:24:14 EDT 2019
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r96579:52462bc541a3
Date: 2019-05-08 15:23 +0200
http://bitbucket.org/pypy/pypy/changeset/52462bc541a3/
Log: hg merge shadowstack-issue2722
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -49,12 +49,19 @@
reserves 768KB of stack space, which should suffice (on Linux,
depending on the compiler settings) for ~1400 calls. Setting the
value to N reserves N/1000 times 768KB of stack space.
+
+Note that there are other factors that also limit the stack size.
+The operating system typically sets a maximum which can be changed
+manually (e.g. with "ulimit" on Linux) for the main thread. For other
+threads you can configure the limit by calling "threading.stack_size()".
"""
from rpython.rlib.rstack import _stack_set_length_fraction
+ from rpython.rlib.rgc import increase_root_stack_depth
if new_limit <= 0:
raise oefmt(space.w_ValueError, "recursion limit must be positive")
space.sys.recursionlimit = new_limit
_stack_set_length_fraction(new_limit * 0.001)
+ increase_root_stack_depth(int(new_limit * 0.001 * 163840))
def getrecursionlimit(space):
"""Return the last value set by setrecursionlimit().
diff --git a/rpython/memory/gctransform/framework.py b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -572,6 +572,8 @@
self.move_out_of_nursery_ptr = getfn(GCClass.move_out_of_nursery,
[s_gc, SomeAddress()],
SomeAddress())
+ if hasattr(self.root_walker, 'build_increase_root_stack_depth_ptr'):
+ self.root_walker.build_increase_root_stack_depth_ptr(getfn)
def create_custom_trace_funcs(self, gc, rtyper):
@@ -1652,6 +1654,12 @@
else:
hop.rename("same_as")
+ def gct_gc_increase_root_stack_depth(self, hop):
+ if not hasattr(self.root_walker, 'gc_increase_root_stack_depth_ptr'):
+ return
+ hop.genop("direct_call",
+ [self.root_walker.gc_increase_root_stack_depth_ptr,
+ hop.spaceop.args[0]])
class TransformerLayoutBuilder(gctypelayout.TypeLayoutBuilder):
diff --git a/rpython/memory/gctransform/shadowstack.py b/rpython/memory/gctransform/shadowstack.py
--- a/rpython/memory/gctransform/shadowstack.py
+++ b/rpython/memory/gctransform/shadowstack.py
@@ -140,6 +140,7 @@
# this is a dict {tid: SHADOWSTACKREF}, where the tid for the
# current thread may be missing so far
gcdata.thread_stacks = None
+ shadow_stack_pool.has_threads = True
# Return the thread identifier, as an integer.
get_tid = rthread.get_ident
@@ -252,6 +253,15 @@
self.gc_modified_shadowstack_ptr = getfn(gc_modified_shadowstack,
[], annmodel.s_None)
+ def build_increase_root_stack_depth_ptr(self, getfn):
+ shadow_stack_pool = self.shadow_stack_pool
+ def gc_increase_root_stack_depth(new_size):
+ shadow_stack_pool.increase_root_stack_depth(new_size)
+
+ self.gc_increase_root_stack_depth_ptr = getfn(
+ gc_increase_root_stack_depth, [annmodel.SomeInteger()],
+ annmodel.s_None)
+
def postprocess_graph(self, gct, graph, any_inlining):
from rpython.memory.gctransform import shadowcolor
if any_inlining:
@@ -269,6 +279,7 @@
"""
_alloc_flavor_ = "raw"
root_stack_depth = 163840
+ has_threads = False
def __init__(self, gcdata):
self.unused_full_stack = llmemory.NULL
@@ -337,6 +348,44 @@
if self.unused_full_stack == llmemory.NULL:
raise MemoryError
+ def increase_root_stack_depth(self, new_depth):
+ if new_depth <= self.root_stack_depth:
+ return # can't easily decrease the size
+ if self.unused_full_stack:
+ llmemory.raw_free(self.unused_full_stack)
+ self.unused_full_stack = llmemory.NULL
+ used = self.gcdata.root_stack_top - self.gcdata.root_stack_base
+ addr = self._resize(self.gcdata.root_stack_base, used, new_depth)
+ self.gcdata.root_stack_base = addr
+ self.gcdata.root_stack_top = addr + used
+ # no gc operations above: we just switched shadowstacks
+ if self.has_threads:
+ self._resize_thread_shadowstacks(new_depth)
+ self.root_stack_depth = new_depth
+
+ def _resize_thread_shadowstacks(self, new_depth):
+ if self.gcdata.thread_stacks is not None:
+ for ssref in self.gcdata.thread_stacks.values():
+ if ssref.base:
+ used = ssref.top - ssref.base
+ addr = self._resize(ssref.base, used, new_depth)
+ ssref.base = addr
+ ssref.top = addr + used
+ _resize_thread_shadowstacks._dont_inline_ = True
+
+ def _resize(self, base, used, new_depth):
+ new_size = sizeofaddr * new_depth
+ ll_assert(used <= new_size, "shadowstack resize: overflow detected")
+ addr = llmemory.raw_malloc(new_size)
+ if addr == llmemory.NULL:
+ raise MemoryError
+ # note that we don't know the total memory size of 'base', but we
+ # know the size of the part that is used right now, and we only need
+ # to copy that
+ llmemory.raw_memmove(base, addr, used)
+ llmemory.raw_free(base)
+ return addr
+
def get_shadowstackref(root_walker, gctransformer):
if hasattr(gctransformer, '_SHADOWSTACKREF'):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -690,6 +690,13 @@
hop.exception_cannot_occur()
return hop.genop('gc_move_out_of_nursery', hop.args_v, resulttype=hop.r_result)
+ at jit.dont_look_inside
+def increase_root_stack_depth(new_depth):
+ """Shadowstack: make sure the size of the shadowstack is at least
+ 'new_depth' pointers."""
+ from rpython.rtyper.lltypesystem.lloperation import llop
+ llop.gc_increase_root_stack_depth(lltype.Void, new_depth)
+
# ____________________________________________________________
diff --git a/rpython/rtyper/llinterp.py b/rpython/rtyper/llinterp.py
--- a/rpython/rtyper/llinterp.py
+++ b/rpython/rtyper/llinterp.py
@@ -1226,6 +1226,9 @@
def op_gc_move_out_of_nursery(self, obj):
raise NotImplementedError("gc_move_out_of_nursery")
+ def op_gc_increase_root_stack_depth(self, new_depth):
+ raise NotImplementedError("gc_increase_root_stack_depth")
+
def op_revdb_stop_point(self, *args):
pass
def op_revdb_send_answer(self, *args):
diff --git a/rpython/rtyper/lltypesystem/lloperation.py b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -530,6 +530,7 @@
'gc_rawrefcount_next_dead': LLOp(),
'gc_move_out_of_nursery': LLOp(),
+ 'gc_increase_root_stack_depth': LLOp(canrun=True),
'gc_push_roots' : LLOp(), # temporary: list of roots to save
'gc_pop_roots' : LLOp(), # temporary: list of roots to restore
diff --git a/rpython/rtyper/lltypesystem/opimpl.py b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -776,6 +776,9 @@
def op_gc_move_out_of_nursery(obj):
return obj
+def op_gc_increase_root_stack_depth(new_depth):
+ pass
+
def op_revdb_do_next_call():
pass
diff --git a/rpython/translator/c/gc.py b/rpython/translator/c/gc.py
--- a/rpython/translator/c/gc.py
+++ b/rpython/translator/c/gc.py
@@ -452,7 +452,6 @@
#
yield ('typedef struct { void %s; } pypy_ss_t;'
% ', '.join(['*s%d' % i for i in range(numcolors)]))
- yield 'pypy_ss_t *ss;'
funcgen.gcpol_ss = gcpol_ss
def OP_GC_PUSH_ROOTS(self, funcgen, op):
@@ -462,26 +461,27 @@
raise Exception("gc_pop_roots should be removed by postprocess_graph")
def OP_GC_ENTER_ROOTS_FRAME(self, funcgen, op):
- return 'ss = (pypy_ss_t *)%s; %s = (void *)(ss+1);' % (
- funcgen.gcpol_ss, funcgen.gcpol_ss)
+ return '%s += sizeof(pypy_ss_t);' % (funcgen.gcpol_ss,)
def OP_GC_LEAVE_ROOTS_FRAME(self, funcgen, op):
- return '%s = (void *)ss;' % funcgen.gcpol_ss
+ return '%s -= sizeof(pypy_ss_t);' % (funcgen.gcpol_ss,)
def OP_GC_SAVE_ROOT(self, funcgen, op):
num = op.args[0].value
exprvalue = funcgen.expr(op.args[1])
- return 'ss->s%d = (void *)%s;\t/* gc_save_root */' % (num, exprvalue)
+ return '((pypy_ss_t *)%s)[-1].s%d = (void *)%s;' % (
+ funcgen.gcpol_ss, num, exprvalue)
def OP_GC_RESTORE_ROOT(self, funcgen, op):
num = op.args[0].value
exprvalue = funcgen.expr(op.args[1])
typename = funcgen.db.gettype(op.args[1].concretetype)
- result = '%s = (%s)ss->s%d;' % (exprvalue, cdecl(typename, ''), num)
+ result = '%s = (%s)((pypy_ss_t *)%s)[-1].s%d;' % (
+ exprvalue, cdecl(typename, ''), funcgen.gcpol_ss, num)
if isinstance(op.args[1], Constant):
- return '/* %s\t* gc_restore_root */' % result
+ return '/* %s */' % result
else:
- return '%s\t/* gc_restore_root */' % result
+ return result
class AsmGcRootFrameworkGcPolicy(BasicFrameworkGcPolicy):
diff --git a/rpython/translator/c/test/test_newgc.py b/rpython/translator/c/test/test_newgc.py
--- a/rpython/translator/c/test/test_newgc.py
+++ b/rpython/translator/c/test/test_newgc.py
@@ -1912,6 +1912,45 @@
def test_total_gc_time(self):
res = self.run("total_gc_time")
assert res > 0 # should take a few microseconds
+
+ def define_increase_root_stack_depth(cls):
+ class X:
+ pass
+ def g(n):
+ if n <= 0:
+ return None
+ x = X()
+ x.n = n
+ x.next = g(n - 1)
+ return x
+ def f(depth):
+ from rpython.rlib.rstack import _stack_set_length_fraction
+ _stack_set_length_fraction(50.0)
+ # ^^^ the default is enough for at least 10'000 (but less than
+ # 100'000) recursions of the simple function g(). We multiply
+ # it by 50.0 to make sure that 200'000 works. The default
+ # shadowstack depth is 163'840 entries, so 200'000 overflows
+ # that default shadowstack depth, and gives a segfault unless
+ # the following line works too.
+ from rpython.rlib.rgc import increase_root_stack_depth
+ increase_root_stack_depth(depth + 100)
+ #
+ g(depth)
+ return 42
+ return f
+
+ def test_increase_root_stack_depth(self):
+ if not sys.platform.startswith('linux'):
+ py.test.skip("linux only")
+ #
+ def myrunner(args):
+ args1 = ['/bin/bash', '-c', 'ulimit -s unlimited && %s' %
+ (' '.join(args),)]
+ return subprocess.check_output(args1)
+ res = self.run("increase_root_stack_depth", 200000, runner=myrunner)
+ assert res == 42
+
+
# ____________________________________________________________________
class TaggedPointersTest(object):
More information about the pypy-commit
mailing list