Sitelet https://github.com/python/cpython/issues/151039
Skip to content

Crash: NULL deref in _datetime when a static type outlives its module #151039

Description

@ashm-dev

What happened?

NULL pointer dereference in the _datetime C module → SIGSEGV. Reproducer:

import sys, gc
import _datetime

td = _datetime.timedelta            # static C type, survives the module
del sys.modules['_datetime']
del _datetime
sys.modules['_datetime'] = None     # block re-import
gc.collect()                        # module object is collected

td(seconds=2)                       # -> Segmentation fault

gdb at the crash: st == (datetime_state *) 0x0, with an ImportError already pending.

Root cause

The datetime types are static and can outlive the module, so per-module state (gh-117398) is fetched via _get_current_state(), which can return NULL when the module weakref is dead and re-import fails (blocked in sys.modules):

static datetime_state *
_get_current_state(PyObject **p_mod)
{
PyInterpreterState *interp = PyInterpreterState_Get();
PyObject *mod = get_current_module(interp);
if (mod == NULL) {
assert(!PyErr_Occurred());
if (PyErr_Occurred()) {
return NULL;
}
/* The static types can outlive the module,
* so we must re-import the module. */
mod = PyImport_ImportModule("_datetime");
if (mod == NULL) {
return NULL;
}
}
datetime_state *st = get_module_state(mod);
*p_mod = mod;
return st;
}

Callers dereference the returned state without a NULL check. At the crash site CONST_US_PER_SECOND(st) is st->us_per_second, so st == NULL → NULL deref:

datetime_state *st = GET_CURRENT_STATE(current_mod);
PyObject *x = NULL; /* running sum of microseconds */
PyObject *y = NULL; /* temp sum of microseconds */
double leftover_us = 0.0;
x = PyLong_FromLong(0);
if (x == NULL)
goto Done;
#define CLEANUP \
Py_DECREF(x); \
x = y; \
if (x == NULL) \
goto Done
if (microseconds) {
y = accum("microseconds", x, microseconds, _PyLong_GetOne(), &leftover_us);
CLEANUP;
}
if (milliseconds) {
y = accum("milliseconds", x, milliseconds, CONST_US_PER_MS(st), &leftover_us);
CLEANUP;
}
if (seconds) {
y = accum("seconds", x, seconds, CONST_US_PER_SECOND(st), &leftover_us);

Originally hit via ./python -X lazy_imports=all -m test test_datetime (segfault in test_strptime; test_datetime is also listed in #149640).

Versions

  • CPython main (3.16.0a0, ab930175e7e). Present since 3.14 (Isolate the _datetime extension module #117398) → 3.14, 3.15, main. Linux.
  • Build: CC=/opt/llvm/bin/clang CXX=/opt/llvm/bin/clang++ LDFLAGS='-fuse-ld=lld' ./configure; clang/lld 23.0.0git (4695c84).

Linked PRs

Activity

  1. changed the title [-]Crash: NULL deref in _datetime when a static datetime type outlives its module (GET_CURRENT_STATE callers don't check for NULL)[/-] [+]Crash: NULL deref in _datetime when a static type outlives its module[/+] on Jun 7, 2026
  2. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Jun 7, 2026
  3. self-assigned this
    on Jun 7, 2026
  4. added 4 commits that reference this issue on Jun 7, 2026
  5. added a commit that references this issue on Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

extension-modulesC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dump

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions