Skip to content

Assertion failures from calling create_builtin with invalid object #142029

Description

@devdanzin

Crash report

What happened?

It's possible to abort the interpreter by running the code below:

import importlib

class S(str): pass
class A: pass

a = A()
a.name = S("123")
importlib._imp.create_builtin(a)

It's also possible to get a different abort by running this code:

import importlib

class S(str): pass
class A: pass

a = A()
a.name = S()
importlib._imp.create_builtin(a)

Backtrace for the first abort:

python: Python/import.c:2438: PyObject *create_builtin(PyThreadState *, PyObject *, PyObject *, PyModInitFunction): Assertion `is_core_module(tstate->interp, info.name, info.path)' failed.

Program received signal SIGABRT, Aborted.

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3  0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5  0x00007ffff7c287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
#6  0x00007ffff7c3c19f in __assert_fail (assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:127
#7  0x00005555558c6a4a in create_builtin (tstate=tstate@entry=0x555555d988c0 <_PyRuntime+358640>, name=name@entry=0x7ffff6d2f580, spec=spec@entry=0x7ffff6c222c0, initfunc=initfunc@entry=0x0)
    at Python/import.c:2438
#8  0x00005555558cc023 in _imp_create_builtin (module=0x7ffff6d6e840, spec=0x7ffff6c222c0) at Python/import.c:4423
#9  0x00005555557136f5 in cfunction_vectorcall_O (func=func@entry=0x7ffff6d8a2d0, args=args@entry=0x7fffffffc468, nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0)
    at Objects/methodobject.c:536
#10 0x0000555555698e0e in _PyObject_VectorcallTstate (tstate=0x555555d988c0 <_PyRuntime+358640>, callable=0x7ffff6d8a2d0, args=0x7fffffffc468, nargsf=9223372036854775809, kwnames=0x0)
    at ./Include/internal/pycore_call.h:169
#11 0x0000555555836303 in _PyEval_EvalFrameDefault (tstate=tstate@entry=0x555555d988c0 <_PyRuntime+358640>, frame=<optimized out>, frame@entry=0x7ffff7fa7020, throwflag=throwflag@entry=0)
    at Python/generated_cases.c.h:1620

Backtrace for the second abort:

python: ./Python/importdl.c:159: int _Py_ext_module_loader_info_init_for_builtin(struct _Py_ext_module_loader_info *, PyObject *): Assertion `PyUnicode_GetLength(name) > 0' failed.

Program received signal SIGABRT, Aborted.

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3  0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5  0x00007ffff7c287f0 in __assert_fail_base (fmt=<optimized out>, assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:118
#6  0x00007ffff7c3c19f in __assert_fail (assertion=<optimized out>, file=<optimized out>, line=<optimized out>, function=<optimized out>) at ./assert/assert.c:127
#7  0x00005555558cd986 in _Py_ext_module_loader_info_init_for_builtin (info=info@entry=0x7fffffffc228, name=name@entry=0x7ffff6d2f580) at ./Python/importdl.c:159
#8  0x00005555558c6696 in create_builtin (tstate=tstate@entry=0x555555d988c0 <_PyRuntime+358640>, name=0x53465, name@entry=0x7ffff6d2f580, spec=0x6, spec@entry=0x7ffff6c222c0,
    initfunc=0x7ffff7ca5fed <__GI___pthread_kill+285>, initfunc@entry=0x0) at Python/import.c:2405
#9  0x00005555558cc023 in _imp_create_builtin (module=0x7ffff6d6e840, spec=0x7ffff6c222c0) at Python/import.c:4423
#10 0x00005555557136f5 in cfunction_vectorcall_O (func=func@entry=0x7ffff6d8a2d0, args=args@entry=0x7fffffffc468, nargsf=nargsf@entry=9223372036854775809, kwnames=kwnames@entry=0x0)
    at Objects/methodobject.c:536
#11 0x0000555555698e0e in _PyObject_VectorcallTstate (tstate=0x555555d988c0 <_PyRuntime+358640>, callable=0x7ffff6d8a2d0, args=0x7fffffffc468, nargsf=9223372036854775809, kwnames=0x0)
    at ./Include/internal/pycore_call.h:169

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a2+ (heads/main-dirty:bc9e63dd9d2, Nov 26 2025, 19:23:56) [Clang 21.1.2 (2ubuntu6)]

Linked PRs

Activity

  1. dr-carlos commented on Nov 28, 2025

    @dr-carlos
    Contributor

    Can reproduce with main branch on Linux. Note that the new S class is unnecessary - it will crash with normal strings too.

    Doesn't seem to occur in 3.14 but I've only checked with the prebuilt binary and not the latest development version.

    With regards to the second part:
    Seems that the call checks that name is a string, but nothing more:

    cpython/Python/import.c

    Lines 4410 to 4423 in 5ec03cf

    PyObject *name = PyObject_GetAttrString(spec, "name");
    if (name == NULL) {
    return NULL;
    }
    if (!PyUnicode_Check(name)) {
    PyErr_Format(PyExc_TypeError,
    "name must be string, not %.200s",
    Py_TYPE(name)->tp_name);
    Py_DECREF(name);
    return NULL;
    }
    PyObject *mod = create_builtin(tstate, name, spec, NULL);

    But in the underlying implementation, the length of the string is also checked:

    cpython/Python/importdl.c

    Lines 158 to 159 in 5ec03cf

    assert(PyUnicode_Check(name));
    assert(PyUnicode_GetLength(name) > 0);

  2. dr-carlos commented on Nov 28, 2025

    @dr-carlos
    Contributor

    The first issue seems to be a regression in 1e4e59b

    It used to be that if a module was not found within the init table (i.e. it was not a built-in module), create_builtin() would return None. However, when this codeblock was rewritten, the case where name is not found automatically assumes that it is an internal module (sys or builtins).

    cpython/Python/import.c

    Lines 2433 to 2442 in 5ec03cf

    PyModInitFunction p0 = initfunc;
    if (p0 == NULL) {
    p0 = lookup_inittab_initfunc(&info);
    if (p0 == NULL) {
    /* Cannot re-init internal module ("sys" or "builtins") */
    assert(is_core_module(tstate->interp, info.name, info.path));
    mod = import_add_module(tstate, info.name);
    goto finally;
    }
    }

  3. dr-carlos commented on Nov 28, 2025

    @dr-carlos
    Contributor

    Adding a check for being a core module seems to fix the first issue:

    • Reverting to the previous behaviour of returning None (instead of crashing) if not found
    • Still performing the current behaviour under any circumstance that wouldn't currently crash.
    @@ -2435,9 +2435,14 @@ create_builtin(
             p0 = lookup_inittab_initfunc(&info);
             if (p0 == NULL) {
                 /* Cannot re-init internal module ("sys" or "builtins") */
    -            assert(is_core_module(tstate->interp, info.name, info.path));
    -            mod = import_add_module(tstate, info.name);
    -            goto finally;
    +            if (is_core_module(tstate->interp, info.name, info.path)) {
    +                mod = import_add_module(tstate, info.name);
    +                goto finally;
    +            }
    +            else {
    +                mod = Py_NewRef(Py_None);
    +                goto finally;
    +            }
             }
         }

    Happy to open a PR if that's warranted.

  4. added a commit that references this issue on Dec 1, 2025
  5. itamaro commented on Dec 1, 2025

    @itamaro
    Contributor

    sorry about the regression from gh-139196! I missed that there was different handling for the case of "no matching inittab entry" vs "found inittab entry but the initfunc is NULL". it wasn't an intentional omission.

    I wrote itamaro@2b839b9 to restore the intended behavior. @dr-carlos since you already have a PR going, it might be best if you integrate my changes into your PR.

  6. dr-carlos commented on Dec 1, 2025

    @dr-carlos
    Contributor

    sorry about the regression from gh-139196! I missed that there was different handling for the case of "no matching inittab entry" vs "found inittab entry but the initfunc is NULL". it wasn't an intentional omission.

    No problem! Thanks for the clarification.

    I wrote itamaro@2b839b9 to restore the intended behavior. @dr-carlos since you already have a PR going, it might be best if you integrate my changes into your PR.

    And thanks for a far more robust fix than my patch. I've now integrated it into #142054!

  7. added a commit that references this issue on Dec 10, 2025
  8. added a commit that references this issue on Dec 15, 2025
  9. vstinner commented on Dec 15, 2025

    @vstinner
    Member

    Thanks for your bug report, both issues have been fixed.

  10. added a commit that references this issue on Dec 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-importlibtype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions