[pypy-commit] pypy default: Check for NULL returns from calls to the raw-malloc, even if they are

arigo pypy.commits at gmail.com
Mon Jul 18 10:55:08 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r85757:54dc4d5466e7
Date: 2016-07-18 15:24 +0200
http://bitbucket.org/pypy/pypy/changeset/54dc4d5466e7/

Log:	Check for NULL returns from calls to the raw-malloc, even if they
	are usually removed by the optimizer. Done not as a regular guard,
	because we don't know where to go if that guard fails, but instead
	done with the same trick as CALL_MALLOC_GC: if it fails, we abort
	the whole loop and raise MemoryError outside. It's still better than
	the current situation, where a failing raw-malloc is ignored and
	then we segfault when trying to use the NULL pointer.

diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -408,6 +408,9 @@
         deadframe = lltype.cast_opaque_ptr(jitframe.JITFRAMEPTR, deadframe)
         descr = deadframe.jf_descr
         res = history.AbstractDescr.show(self, descr)
+        if not we_are_translated():     # for missing propagate_exception_descr
+            if res is None:
+                raise MissingLatestDescrError
         assert isinstance(res, history.AbstractFailDescr)
         return res
 
@@ -816,6 +819,9 @@
         calldescr.call_stub_i(func, args_i, args_r, args_f)
 
 
+class MissingLatestDescrError(Exception):
+    """For propagate_exception_descr in untranslated tests."""
+
 final_descr_rd_locs = [rffi.cast(rffi.USHORT, 0)]
 history.BasicFinalDescr.rd_locs = final_descr_rd_locs
 compile._DoneWithThisFrameDescr.rd_locs = final_descr_rd_locs
diff --git a/rpython/jit/backend/test/runner_test.py b/rpython/jit/backend/test/runner_test.py
--- a/rpython/jit/backend/test/runner_test.py
+++ b/rpython/jit/backend/test/runner_test.py
@@ -22,6 +22,7 @@
 from rpython.jit.backend.detect_cpu import autodetect
 from rpython.jit.backend.llsupport import jitframe
 from rpython.jit.backend.llsupport.llmodel import AbstractLLCPU
+from rpython.jit.backend.llsupport.llmodel import MissingLatestDescrError
 from rpython.jit.backend.llsupport.rewrite import GcRewriterAssembler
 
 
@@ -4391,6 +4392,25 @@
                          'float', descr=calldescr)
             assert longlong.getrealfloat(res) == expected
 
+    def test_raw_malloc_out_of_memory(self):
+        def failing_malloc(size):
+            return 0
+        effectinfo = EffectInfo([], [], [], [], [], [],
+                                EffectInfo.EF_CAN_RAISE,
+                                EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR)
+        FPTR = self.Ptr(self.FuncType([lltype.Signed], lltype.Signed))
+        func_ptr = llhelper(FPTR, failing_malloc)
+        FUNC = deref(FPTR)
+        funcbox = self.get_funcbox(self.cpu, func_ptr)
+        calldescr = self.cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+                                         effectinfo)
+        # Executing a CALL_I to the OS_RAW_MALLOC_VARSIZE_CHAR should
+        # be special-cased so that a return value of 0 triggers a
+        # get-out-of-loop MemoryError, like a failing CALL_MALLOC_GC
+        py.test.raises(MissingLatestDescrError,
+            self.execute_operation, rop.CALL_I,
+            [funcbox, InputArgInt(12345)], 'void', descr=calldescr)
+
     def test_compile_loop_with_target(self):
         looptoken = JitCellToken()
         targettoken1 = TargetToken()
diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -292,9 +292,6 @@
         return rawstart
 
     def _build_propagate_exception_path(self):
-        if not self.cpu.propagate_exception_descr:
-            return      # not supported (for tests, or non-translated)
-        #
         self.mc = codebuf.MachineCodeBlockWrapper()
         self.mc.force_frame_size(DEFAULT_FRAME_BYTES)
         #
@@ -303,7 +300,11 @@
         self._store_and_reset_exception(self.mc, eax)
         ofs = self.cpu.get_ofs_of_frame_field('jf_guard_exc')
         self.mc.MOV_br(ofs, eax.value)
-        propagate_exception_descr = rffi.cast(lltype.Signed,
+        if not self.cpu.propagate_exception_descr:
+            # for tests, or non-translated
+            propagate_exception_descr = 0
+        else:
+            propagate_exception_descr = rffi.cast(lltype.Signed,
                   cast_instance_to_gcref(self.cpu.propagate_exception_descr))
         ofs = self.cpu.get_ofs_of_frame_field('jf_descr')
         self.mc.MOV(RawEbpLoc(ofs), imm(propagate_exception_descr))
diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -884,6 +884,8 @@
             if oopspecindex == EffectInfo.OS_MATH_READ_TIMESTAMP:
                 return self._consider_math_read_timestamp(op)
         self._consider_call(op)
+        if oopspecindex == EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR:
+            self.assembler.propagate_memoryerror_if_eax_is_null()
     consider_call_i = _consider_real_call
     consider_call_r = _consider_real_call
     consider_call_f = _consider_real_call
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -1818,7 +1818,6 @@
         [i1]
         label(i1)
         i2 = call_i('malloc', 20, descr=raw_malloc_descr)
-        #guard_no_exception() []  # XXX should appear
         raw_store(i2, 0, i1, descr=rawarraydescr_char)
         raw_store(i2, 1, 123, descr=rawarraydescr_char)
         raw_store(i2, 2, 456, descr=rawarraydescr_char)
@@ -1844,7 +1843,6 @@
         [i1]
         label(i1)
         i2 = call_i('malloc', 10, descr=raw_malloc_descr)
-        #guard_no_exception() []  # XXX should appear
         raw_store(i2, 0, i1, descr=rawarraydescr)
         setarrayitem_raw(i2, 2, 456, descr=rawarraydescr_char)
         call_n('free', i2, descr=raw_free_descr)
@@ -1867,7 +1865,6 @@
         [i1]
         label(i1)
         i2 = call_i('malloc', 10, descr=raw_malloc_descr)
-        #guard_no_exception() []  # XXX should appear
         raw_store(i2, 0, i1, descr=rawarraydescr)
         i3 = getarrayitem_raw_i(i2, 0, descr=rawarraydescr_char)
         call_n('free', i2, descr=raw_free_descr)
@@ -1930,7 +1927,6 @@
         label(i0, i1)
         # these ops are generated by VirtualRawBufferValue._really_force
         i2 = call_i('malloc', 10, descr=raw_malloc_descr)
-        #guard_no_exception() []  # XXX should appear
         raw_store(i2, 0, 42, descr=rawarraydescr_char)
         raw_store(i2, 5, 4242, descr=rawarraydescr_char)
         # this is generated by VirtualRawSliceValue._really_force
@@ -1959,7 +1955,6 @@
         call_n('free', i0, descr=raw_free_descr)
         label(i2)
         i3 = call_i('malloc', 10, descr=raw_malloc_descr)
-        #guard_no_exception() []  # XXX should appear
         raw_store(i3, 0, i2, descr=rawarraydescr)
         jump(i3)
         """


More information about the pypy-commit mailing list