Commit fa8db663 authored by da-woods's avatar da-woods Committed by GitHub

Avoid AddTraceback() if stringtab isn't set up (GH-4378)

This can happen (rarely) with exceptions that occur very early in the module init process.

Fixes https://github.com/cython/cython/issues/4377

Uses a fake Numpy module for testing to make a version of "import_array" that always fails.
parent f94f26a0
...@@ -1138,7 +1138,8 @@ class GlobalState(object): ...@@ -1138,7 +1138,8 @@ class GlobalState(object):
'pystring_table', 'pystring_table',
'cached_builtins', 'cached_builtins',
'cached_constants', 'cached_constants',
'init_globals', 'init_constants',
'init_globals', # (utility code called at init-time)
'init_module', 'init_module',
'cleanup_globals', 'cleanup_globals',
'cleanup_module', 'cleanup_module',
...@@ -1209,6 +1210,11 @@ class GlobalState(object): ...@@ -1209,6 +1210,11 @@ class GlobalState(object):
w.putln("") w.putln("")
w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) {") w.putln("static CYTHON_SMALL_CODE int __Pyx_InitGlobals(void) {")
w = self.parts['init_constants']
w.enter_cfunc_scope()
w.putln("")
w.putln("static CYTHON_SMALL_CODE int __Pyx_InitConstants(void) {")
if not Options.generate_cleanup_code: if not Options.generate_cleanup_code:
del self.parts['cleanup_globals'] del self.parts['cleanup_globals']
else: else:
...@@ -1284,13 +1290,14 @@ class GlobalState(object): ...@@ -1284,13 +1290,14 @@ class GlobalState(object):
w.putln("}") w.putln("}")
w.exit_cfunc_scope() w.exit_cfunc_scope()
w = self.parts['init_globals'] for part in ['init_globals', 'init_constants']:
w.putln("return 0;") w = self.parts[part]
if w.label_used(w.error_label): w.putln("return 0;")
w.put_label(w.error_label) if w.label_used(w.error_label):
w.putln("return -1;") w.put_label(w.error_label)
w.putln("}") w.putln("return -1;")
w.exit_cfunc_scope() w.putln("}")
w.exit_cfunc_scope()
if Options.generate_cleanup_code: if Options.generate_cleanup_code:
w = self.parts['cleanup_globals'] w = self.parts['cleanup_globals']
...@@ -1510,7 +1517,7 @@ class GlobalState(object): ...@@ -1510,7 +1517,7 @@ class GlobalState(object):
return return
decl = self.parts['decls'] decl = self.parts['decls']
init = self.parts['init_globals'] init = self.parts['init_constants']
cnames = [] cnames = []
for (type_cname, method_name), cname in sorted(self.cached_cmethods.items()): for (type_cname, method_name), cname in sorted(self.cached_cmethods.items()):
cnames.append(cname) cnames.append(cname)
...@@ -1560,7 +1567,7 @@ class GlobalState(object): ...@@ -1560,7 +1567,7 @@ class GlobalState(object):
decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf16_array)) decls_writer.putln("static Py_UNICODE %s[] = { %s };" % (cname, utf16_array))
decls_writer.putln("#endif") decls_writer.putln("#endif")
init_globals = self.parts['init_globals'] init_constants = self.parts['init_constants']
if py_strings: if py_strings:
self.use_utility_code(UtilityCode.load_cached("InitStrings", "StringTools.c")) self.use_utility_code(UtilityCode.load_cached("InitStrings", "StringTools.c"))
py_strings.sort() py_strings.sort()
...@@ -1575,9 +1582,9 @@ class GlobalState(object): ...@@ -1575,9 +1582,9 @@ class GlobalState(object):
decls_writer.putln("#if !CYTHON_USE_MODULE_STATE") decls_writer.putln("#if !CYTHON_USE_MODULE_STATE")
not_limited_api_decls_writer = decls_writer.insertion_point() not_limited_api_decls_writer = decls_writer.insertion_point()
decls_writer.putln("#endif") decls_writer.putln("#endif")
init_globals.putln("#if CYTHON_USE_MODULE_STATE") init_constants.putln("#if CYTHON_USE_MODULE_STATE")
init_globals_in_module_state = init_globals.insertion_point() init_constants_in_module_state = init_constants.insertion_point()
init_globals.putln("#endif") init_constants.putln("#endif")
for idx, py_string_args in enumerate(py_strings): for idx, py_string_args in enumerate(py_strings):
c_cname, _, py_string = py_string_args c_cname, _, py_string = py_string_args
if not py_string.is_str or not py_string.encoding or \ if not py_string.is_str or not py_string.encoding or \
...@@ -1627,20 +1634,20 @@ class GlobalState(object): ...@@ -1627,20 +1634,20 @@ class GlobalState(object):
py_string.is_str, py_string.is_str,
py_string.intern py_string.intern
)) ))
init_globals_in_module_state.putln("if (__Pyx_InitString(%s[%d], &%s) < 0) %s;" % ( init_constants_in_module_state.putln("if (__Pyx_InitString(%s[%d], &%s) < 0) %s;" % (
Naming.stringtab_cname, Naming.stringtab_cname,
idx, idx,
py_string.cname, py_string.cname,
init_globals.error_goto(self.module_pos))) init_constants.error_goto(self.module_pos)))
w.putln("{0, 0, 0, 0, 0, 0, 0}") w.putln("{0, 0, 0, 0, 0, 0, 0}")
w.putln("};") w.putln("};")
init_globals.putln("#if !CYTHON_USE_MODULE_STATE") init_constants.putln("#if !CYTHON_USE_MODULE_STATE")
init_globals.putln( init_constants.putln(
"if (__Pyx_InitStrings(%s) < 0) %s;" % ( "if (__Pyx_InitStrings(%s) < 0) %s;" % (
Naming.stringtab_cname, Naming.stringtab_cname,
init_globals.error_goto(self.module_pos))) init_constants.error_goto(self.module_pos)))
init_globals.putln("#endif") init_constants.putln("#endif")
def generate_num_constants(self): def generate_num_constants(self):
consts = [(c.py_type, c.value[0] == '-', len(c.value), c.value, c.value_code, c) consts = [(c.py_type, c.value[0] == '-', len(c.value), c.value, c.value_code, c)
...@@ -1648,7 +1655,7 @@ class GlobalState(object): ...@@ -1648,7 +1655,7 @@ class GlobalState(object):
consts.sort() consts.sort()
decls_writer = self.parts['decls'] decls_writer = self.parts['decls']
decls_writer.putln("#if !CYTHON_USE_MODULE_STATE") decls_writer.putln("#if !CYTHON_USE_MODULE_STATE")
init_globals = self.parts['init_globals'] init_constants = self.parts['init_constants']
for py_type, _, _, value, value_code, c in consts: for py_type, _, _, value, value_code, c in consts:
cname = c.cname cname = c.cname
self.parts['module_state'].putln("PyObject *%s;" % cname) self.parts['module_state'].putln("PyObject *%s;" % cname)
...@@ -1669,9 +1676,9 @@ class GlobalState(object): ...@@ -1669,9 +1676,9 @@ class GlobalState(object):
function = "PyInt_FromLong(%sL)" function = "PyInt_FromLong(%sL)"
else: else:
function = "PyInt_FromLong(%s)" function = "PyInt_FromLong(%s)"
init_globals.putln('%s = %s; %s' % ( init_constants.putln('%s = %s; %s' % (
cname, function % value_code, cname, function % value_code,
init_globals.error_goto_if_null(cname, self.module_pos))) init_constants.error_goto_if_null(cname, self.module_pos)))
decls_writer.putln("#endif") decls_writer.putln("#endif")
# The functions below are there in a transition phase only # The functions below are there in a transition phase only
......
...@@ -2939,7 +2939,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -2939,7 +2939,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
# start of module init/exec function (pre/post PEP 489) # start of module init/exec function (pre/post PEP 489)
code.putln("{") code.putln("{")
code.putln('int stringtab_initialized = 0;')
tempdecl_code = code.insertion_point() tempdecl_code = code.insertion_point()
profile = code.globalstate.directives['profile'] profile = code.globalstate.directives['profile']
...@@ -3012,7 +3012,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -3012,7 +3012,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
code.putln("#endif") code.putln("#endif")
code.putln("/*--- Initialize various global constants etc. ---*/") code.putln("/*--- Initialize various global constants etc. ---*/")
code.put_error_if_neg(self.pos, "__Pyx_InitGlobals()") code.put_error_if_neg(self.pos, "__Pyx_InitConstants()")
code.putln("stringtab_initialized = 1;")
code.put_error_if_neg(self.pos, "__Pyx_InitGlobals()") # calls any utility code
code.putln("#if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || " code.putln("#if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || "
"__PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)") "__PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)")
...@@ -3095,7 +3098,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): ...@@ -3095,7 +3098,9 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for cname, type in code.funcstate.all_managed_temps(): for cname, type in code.funcstate.all_managed_temps():
code.put_xdecref(cname, type) code.put_xdecref(cname, type)
code.putln('if (%s) {' % env.module_cname) code.putln('if (%s) {' % env.module_cname)
code.putln('if (%s) {' % env.module_dict_cname) code.putln('if (%s && stringtab_initialized) {' % env.module_dict_cname)
# We can run into errors before the module or stringtab are initialized.
# In this case it is not safe to add a traceback (because it uses the stringtab)
code.put_add_traceback(EncodedString("init %s" % env.qualified_name)) code.put_add_traceback(EncodedString("init %s" % env.qualified_name))
code.globalstate.use_utility_code(Nodes.traceback_utility_code) code.globalstate.use_utility_code(Nodes.traceback_utility_code)
# Module reference and module dict are in global variables which might still be needed # Module reference and module dict are in global variables which might still be needed
......
PYTHON setup.py build_ext -i
PYTHON main.py
############# setup.py ############
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize('cimport_numpy.pyx'))
############# numpy.pxd ############
# A fake Numpy module. This defines a version of _import_array
# that always fails. The Cython-generated call to _import_array
# happens quite early (before the stringtab is initialized)
# and thus the error itself handling could cause a segmentation fault
# https://github.com/cython/cython/issues/4377
cdef extern from *:
"""
#define NPY_NDARRAYOBJECT_H
static int _import_array(void) {
PyErr_SetString(PyExc_ValueError, "Oh no!");
return -1;
}
"""
int _import_array() except -1
############# cimport_numpy.pyx ###########
cimport numpy
############# main.py ####################
try:
import cimport_numpy
except ImportError as e:
print(e)
else:
assert(False)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment