Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

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

Copy link
Copy link
@ashm-dev

Description

@ashm-dev
Issue body actions

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

Reactions are currently unavailable

Metadata

Metadata

Assignees

Labels

extension-modulesC modules in the Modules dirC modules in the Modules dirtype-crashA hard crash of the interpreter, possibly with a core dumpA hard crash of the interpreter, possibly with a core dump
No fields configured for issues without a type.

Projects

Status
No status
Show more project fields

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Morty Proxy This is a proxified and sanitized view of the page, visit original site.