Author: Matti Picus <matti.pi...@gmail.com> Branch: py3.6 Changeset: r96612:646010cf591d Date: 2019-05-13 18:17 -0700 http://bitbucket.org/pypy/pypy/changeset/646010cf591d/
Log: merge default into py3.6 diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -25,3 +25,7 @@ Test and reduce the probability of a deadlock when acquiring a semaphore by moving global state changes closer to the actual aquire. + +.. branch: shadowstack-issue2722 + +Make the shadowstack size more dynamic diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h --- a/pypy/module/cpyext/include/object.h +++ b/pypy/module/cpyext/include/object.h @@ -384,6 +384,16 @@ PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *); #endif +/* + * On CPython with Py_REF_DEBUG these use _PyRefTotal, _Py_NegativeRefcount, + * _Py_GetRefTotal, ... + * So far we ignore Py_REF_DEBUG + */ + +#define _Py_INC_REFTOTAL +#define _Py_DEC_REFTOTAL +#define _Py_REF_DEBUG_COMMA +#define _Py_CHECK_REFCNT(OP) /* a semicolon */; /* PyPy internal ----------------------------------- */ PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *); 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 @@ -55,9 +55,15 @@ 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.rstackovf import StackOverflow + from rpython.rlib.rgc import increase_root_stack_depth if new_limit <= 0: raise oefmt(space.w_ValueError, "recursion limit must be positive") try: @@ -69,6 +75,7 @@ raise oefmt(space.w_RecursionError, "maximum recursion depth exceeded") space.sys.recursionlimit = new_limit + 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) +@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 @@ -450,9 +450,8 @@ # XXX hard-code the field name here gcpol_ss = '%s->gcd_inst_root_stack_top' % funcgen.expr(c_gcdata) # - yield ('typedef struct { void %s; } pypy_ss_t;' + yield ('typedef struct { char %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 = (char *)%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): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit