Skip to content

os.environ.clear() now leaves the C environ pointer NULL, which crashes Tcl/Tk (tkinter) on the next Tcl_CreateInterp #157741

Description

@YuanchengJiang

Crash report

What happened?

Since gh-139482 (PR gh-139965, October 2025) os.environ.clear() calls posix._clearenv() →
clearenv(3). On glibc clearenv() sets the global environ to NULL rather than to an
empty array. Any C library that walks environ without a NULL check then segfaults; Tcl is one
of them (TclSetupEnv in generic/tclEnv.c reads environ[0], in 8.6 and on Tcl main).
Before this change clear() removed keys one at a time with unsetenv(), which leaves
environ a valid empty array, and the program below worked.

The following code:

import os
os.environ.clear()
import tkinter
tkinter.Tcl()

Resulted in this output:

AddressSanitizer:DEADLYSIGNAL
==1527921==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x75e7d758a13f bp 0x78b7d9e5f060 sp 0x7ffdab0f82c0 T0)
==1527921==The signal is caused by a READ memory access.
==1527921==Hint: address points to the zero page.
    #0 0x75e7d758a13f in TclSetupEnv (/lib/x86_64-linux-gnu/libtcl8.6.so+0xb213f)
    #1 0x75e7d75114d8 in Tcl_CreateInterp (/lib/x86_64-linux-gnu/libtcl8.6.so+0x394d8)
    #2 0x75e7d88c1607 in Tkapp_New Modules/_tkinter.c:643:17
    #3 0x75e7d88c1607 in _tkinter_create_impl Modules/_tkinter.c:3483:25
    #4 0x75e7d88c1607 in _tkinter_create Modules/clinic/_tkinter.c.h:898:20
    #5 0x6375e0068750 in cfunction_vectorcall_FASTCALL Objects/methodobject.c:449:24
    #6 0x6375dfeb5205 in _PyObject_VectorcallTstate Include/internal/pycore_call.h:144:11
    #7 0x6375e04005ac in _Py_VectorCallInstrumentation_StackRefSteal Python/ceval.c:770:11
    #8 0x6375e0449521 in _PyEval_EvalFrameDefault Python/generated_cases.c.h:1906:35
    #9 0x6375e03ff857 in _PyEval_Vector Python/ceval.c:2176:12
    #10 0x6375dfeb55b4 in _PyObject_VectorcallDictTstate Objects/call.c:135:15
    #11 0x6375dfeb97e6 in _PyObject_Call_Prepend Objects/call.c:504:24
    #12 0x6375e017d7b0 in call_method Objects/typeobject.c:3070:19
    #13 0x6375e01c9165 in slot_tp_init Objects/typeobject.c:11115:21
    #14 0x6375e0159c7a in type_call Objects/typeobject.c:2454:19

Without a sanitizer it is a plain Segmentation fault. It is deterministic.

Evidence that the NULL pointer is the cause:

import os, ctypes
environ = ctypes.c_void_p.in_dll(ctypes.CDLL(None), "environ")
os.environ.clear()
print(environ.value)          # None  -> environ == NULL

Deleting the keys individually instead keeps environ non-NULL and tkinter.Tcl() succeeds:

import os
for k in list(os.environ):
    del os.environ[k]
import tkinter
tkinter.Tcl()                 # OK

Calling libc.clearenv() directly from ctypes reproduces the crash too, so the library
behaviour is glibc's; the regression is that os.environ.clear() now exposes it to every
tkinter (and any other environ-walking extension) user. Possible fixes on the CPython side:
after clearenv(), point environ at a static {NULL} array (glibc's setenv/putenv copy
rather than realloc an environ they did not allocate, so this is safe), or fall back to the
per-key unsetenv() loop.

To reproduce:

./python min.py

Commit:

e682b4478c2ad09de0bbe821127f451f02f6e5ca  (main, 2026-09-16)

Build configuration:

../configure --with-pydebug --enable-experimental-jit=yes --with-address-sanitizer --with-undefined-behavior-sanitizer

Operating System:

Ubuntu 22.04.5 LTS, x86_64, glibc 2.35, Tcl/Tk 8.6.12 (libtcl8.6 8.6.12+dfsg-1build1), in Docker (image fusion-fuzz-cpython:latest)

This bug was found by fusion-fuzz

CPython versions tested on:

CPython main branch

Operating systems tested on:

No response

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

No response

Linked PRs

Activity

  1. added
    type-crashA hard crash of the interpreter, possibly with a core dump
    on Sep 18, 2026
  2. search1ofall-maker commented on Sep 18, 2026

    @search1ofall-maker

    Hi @YuanchengJiang!

    Thanks for the detailed crash report and ASan stack trace.

    The regression is clear: glibc's clearenv() setting environ = NULL violates the assumption made by libraries like Tcl (TclSetupEnv), which expect a non-NULL environ array.

    I can take this on. The fix in Modules/posixmodule.c (ensuring environ points to an empty {NULL} array after clearenv(), or fallback behavior) should resolve the segfault while preserving clearenv semantics.

    I'll prepare a PR shortly!

  3. aisk commented on Sep 18, 2026

    @aisk
    Member

    If glibc's clearenv() sets environ to NULL, then NULL should be treated as a valid state for environ. Even if we "fix" os.environ.clear() to not set it to NULL, other users' code or extension modules may still call clearenv() and crash tkinter in the same way.

    So I think we can fix this in os as a regression, since it did not leave environ NULL before 3.15, and maybe we should also report this as a bug to Tcl, so that it accepts a NULL environ.

  4. moved this from Todo to In Progress in OS issues 💻on Sep 18, 2026
  5. search1ofall-maker commented on Sep 18, 2026

    @search1ofall-maker

    Makes total sense, @aisk!

    Good point on Tcl — handling NULL on their side would definitely make things more resilient if clearenv() gets called directly in C.

    Glad we're keeping this fix in os to avoid breaking existing code that worked prior to 3.15. I can file an upstream issue for Tcl later as well.

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

    3.15bugs and security fixesextension-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