python / python/cpython

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

Ouverte
#157,741 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

3.15 extension-modules type-crash
Langage dominant
Python
Étoiles
77.2k
Forks
35.9k
Métriques de merge des PR
Métriques de PR en attente

Description

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
  • gh-157745

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par reproduire le crash avec min.py, puis examinez le chemin de os.environ.clear() via posix._clearenv(). Vérifiez le comportement autour du pointeur environ de C et exécutez les tests pertinents de l’environnement et de tkinter ; le travail est terminé lorsque le fait de vider os.environ ne provoque plus le crash de tkinter.Tcl() sur la plateforme concernée.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
c, python
Domaine
operating-systems
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
20/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.