gh-145301: Fix double-free in hashlib and hmac module initialization#145321
gh-145301: Fix double-free in hashlib and hmac module initialization#145321krylosov-aa wants to merge 3 commits intopython:mainfrom
Conversation
| Fix a double-free bug in :mod:`_hashlib` module initialization when | ||
| ``_Py_hashtable_set()`` fails while adding an algorithm alias to the hash | ||
| table after the primary name was already added |
There was a problem hiding this comment.
Too verbose:
| Fix a double-free bug in :mod:`_hashlib` module initialization when | |
| ``_Py_hashtable_set()`` fails while adding an algorithm alias to the hash | |
| table after the primary name was already added | |
| :mod:`hashlib`: fix a crash when the C extension module initialization fails. |
picnixz
left a comment
There was a problem hiding this comment.
Please add the test repro as a regression test.
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
|
Can you also check whether |
I checked the HMAC implementation in |
|
I'm sorry, I meant the |
Yes, I can suggest the following solution: #define Py_HMAC_HINFO_LINK(KEY) \
do { \
int rc = py_hmac_hinfo_ht_add(table, KEY, value); \
if (rc < 0) { \
+ if (value->refcnt == 0) { \
PyMem_Free(value); \
+ } \
goto error; \
} \
else if (rc == 1) { \
value->refcnt++; \
} \
} while (0)
Py_HMAC_HINFO_LINK(e->name);
Py_HMAC_HINFO_LINK(e->hashlib_name);
#undef Py_HMAC_HINFO_LINKShould I include this fix in the same PR or create a separate one? |
|
Yes, please do so, and put a comment as well. |
I investigated adding a regression test using The Do you have any suggestions for how to reliably test this? |
| int rc = py_hmac_hinfo_ht_add(table, KEY, value); \ | ||
| if (rc < 0) { \ | ||
| PyMem_Free(value); \ | ||
| /* entry may already be in ht, will be freed by _Py_hashtable_destroy() */ \ |
There was a problem hiding this comment.
| /* entry may already be in ht, will be freed by _Py_hashtable_destroy() */ \ | |
| /* entry may already be in ht, will be \ | |
| * freed by _Py_hashtable_destroy() */ \ |
- correctly align the
\. You can augment the column if it does not exceed 80 chars. If it does, properly wrap the comment. Tia.
You have an ASASN reproducer, why not use it? |
Both
hashlibandhmacmodules have similar double-free bugs in their hashtable initialization code.Problem
hashlibIn
py_hashentry_table_new(), when_Py_hashtable_set()fails while adding an entry by itspy_aliaskey (after successfully adding it bypy_name), the code callsPyMem_Free(entry)beforegoto error. The error handler then calls_Py_hashtable_destroy()which frees the same entry again viapy_hashentry_t_destroy_value().hmacIn
py_hmac_hinfo_ht_new(), thePy_HMAC_HINFO_LINKmacro has the same issue. Whenpy_hmac_hinfo_ht_add()fails forhashlib_namekey (after successfully addingname), the code callsPyMem_Free(value)while the entry is already in the hashtable withrefcnt=1.Solution
hashlibRemove the manual
PyMem_Free(entry)call since the entry is already tracked by the hashtable under thepy_namekey and will be properly freed by_Py_hashtable_destroy().hmacAdd a
refcntcheck before freeing: only callPyMem_Free(value)ifvalue->refcnt == 0(meaning it was never added to the table). Ifrefcnt > 0, the entry is already in the table and will be freed by_Py_hashtable_destroy().Tests
No test added. I couldn't find a practical way to trigger this bug since it requires
_Py_hashtable_set()to fail during module initialization. This type of bug is best caught by sanitizers rather than unit tests.The fix can be verified by running the reproducer provided by the issue author:
Build with ASan:
Reproducer script (
uaf_asan.py):Before fix:
After fix: No crash.
_hashlib: Use-After-Free + Double Free in_hashopenssl.cpy_hashentry_table_new()#145301