Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork34.2k
Description
Crash report
What happened?
Inpy_hashentry_table_new(), when_Py_hashtable_set() fails for an alias key (line 270),entry is explicitly freed viaPyMem_Free(entry) at line 271. However, this sameentry was already successfully inserted into the hashtable underpy_name at line 263. Thegoto error path then calls_Py_hashtable_destroy(ht) (line 280), which invokes the destroy callbackpy_hashentry_t_destroy_value() on the already-freed entry.
// Modules/_hashopenssl.c - py_hashentry_table_new()for (constpy_hashentry_t*h=py_hashes;h->py_name!=NULL;h++) {py_hashentry_t*entry= (py_hashentry_t*)PyMem_Malloc(sizeof(py_hashentry_t));if (entry==NULL) { gotoerror; }memcpy(entry,h,sizeof(py_hashentry_t));// [line 263] entry inserted into hashtable under py_name - hashtable now owns itif (_Py_hashtable_set(ht, (constvoid*)entry->py_name, (void*)entry)<0) {PyMem_Free(entry); gotoerror; }entry->refcnt=1;if (h->py_alias!=NULL) {// [line 270] second insert fails (e.g. OOM)if (_Py_hashtable_set(ht, (constvoid*)entry->py_alias, (void*)entry)<0) {PyMem_Free(entry);// [line 271] BUG: entry freed, but still in hashtable under py_name gotoerror;// [line 272] jumps to error path }entry->refcnt++; }}returnht;error:_Py_hashtable_destroy(ht);// [line 280] destroy callback called on already-freed entryreturnNULL;
Build
mkdir build-asan&&cd build-asan../configure --with-pydebug --with-address-sanitizer --without-pymallocmake -j$(nproc)
importsubprocessimportsyscode= ("import sys, _testcapi\n""if '_hashlib' in sys.modules:\n"" del sys.modules['_hashlib']\n""_testcapi.set_nomemory(40, 41)\n""try:\n"" import _hashlib\n""except (MemoryError, ImportError):\n"" pass\n""finally:\n"" _testcapi.remove_mem_hooks()\n")result=subprocess.run( [sys.executable,'-c',code],capture_output=True,text=True,timeout=10)ifresult.returncode!=0:print(f"[*] CRASH confirmed (rc={result.returncode})")print(f"[*]{result.stderr.strip().split(chr(10))[-1]}")else:print("[*] No crash (try different start values)")
$ ./build-asan/python uaf_asan.py[*] CRASH confirmed (rc=-6)[*] python: ../Include/internal/pycore_stackref.h:554: PyStackRef_FromPyObjectSteal: Assertion`obj!= NULL' failed.
GDB backtrace
$ gdb -batch -ex run -ex bt --args ./build-asan/python -c"import sys, _testcapiif '_hashlib' in sys.modules: del sys.modules['_hashlib']_testcapi.set_nomemory(40, 41)try: import _hashlibexcept (MemoryError, ImportError): passfinally: _testcapi.remove_mem_hooks()"
python: ../Include/internal/pycore_stackref.h:554: PyStackRef_FromPyObjectSteal: Assertion `obj != NULL' failed.Program received signal SIGABRT, Aborted.#0 __pthread_kill_implementation at ./nptl/pthread_kill.c:44#1 __pthread_kill_internal at ./nptl/pthread_kill.c:78#2 __GI___pthread_kill at ./nptl/pthread_kill.c:89#3 __GI_raise at ../sysdeps/posix/raise.c:26#4 __GI_abort at ./stdlib/abort.c:79#5 __assert_fail_base — Assertion `obj != NULL' failed.#6 __assert_fail at ./assert/assert.c:105#7 PyStackRef_FromPyObjectSteal at ../Include/internal/pycore_stackref.h:554#8 _PyEval_EvalFrameDefault at ../Python/generated_cases.c.h:292...#15 import_find_and_load — importing _hashlib...#19 _PyEval_EvalFrameDefault at ../Python/generated_cases.c.h:6424The assertion fires because memory corruption during_hashlib module init (caused by the UAF/double-free) propagates a NULL into the eval loop.
Suggested Fix
RemovePyMem_Free(entry) at line 271. Theentry is already owned by the hashtable (underpy_name withrefcnt=1), so_Py_hashtable_destroy() in the error path will correctly clean it up via the destroy callback.
if (h->py_alias != NULL) { if (_Py_hashtable_set(ht, (const void*)entry->py_alias, (void*)entry) < 0) {- PyMem_Free(entry); goto error; } entry->refcnt++; }CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.15.0a6+