Fix incorrect Py_XDECREF/Py_DECREF call on stolen reference after PyList_SetItem#557
Fix incorrect Py_XDECREF/Py_DECREF call on stolen reference after PyList_SetItem#557wr-web wants to merge 227 commits into
Py_XDECREF/Py_DECREF call on stolen reference after PyList_SetItem#557Conversation
* Added curl 8.16.0 * Fix and streamline broken ISO build script * Fix potential buffer overflow ravynsoft#438 * Hide console mouse cursor when starting WindowServer * Fix sizing issue in [NSWindow initWithContentRect] * Install zsh in CI since build.sh needs it now * SSHD needs -lSystem now [ci skip] * Always include mach/mach.h for mach syscall defs. * Update _DP_ssh with libSystem * Add libSystem to libssh SUBDIR_DEPEND * Fix /etc/sudoers permissions on ISO * Add options to clean builds first to build.sh * Simplify release logo maintenance * Maybe fix the clean build? * Use Emacs keybindings in zsh by default * Link su with libmach for libnotify * Ensure NS*FunctionKey codes are in UTF8 in mach_event
|
Seems like a bot. Here’s a similar PR, one of many ionelmc/python-hunter#128 |
In the case of ionelmc/python-hunter#128, the reason the project did not accept that PR is because they believed the issue came from third-party generated code that wasn't actually used in the project. However, for the current project, I believe there is a real issue. |
|
Ok, you've explained the bug well, but where is it actually used? This looks specific to libxml2 (I hope you submitted the report to them as well) and only if using it from Python. Right? |
You're right, thanks for the clarification. This issue is indeed specific to libxml2 when used from Python. I'll also report it to the libxml2 team. |
Hi, I found that this issue has been acknowledged and fixed upstream. I think this project should also apply the fix. Could you merge this PR or sync the latest libxml2? |
|
Great, thanks for ensuring it gets upstream! ravynOS does not use libxml2 from python anywhere that I know of (it's only used from C) so I don't believe we have any exposure, but I will merge the latest libxml2 after I get through the current refactoring. |
Related Issue
#556
Description
A reference to item is incorrectly decremented after a failed PyList_SetItem call, leading to a potential use-after-free vulnerability or double-decrement crash.
Affected Code
ravynos/BSD/lib/libxml2/python/libxml.c
Lines 1224 to 1229 in 38faa38
Root Cause
PyList_SetItem Source Code
PyList_SetItem Document
PyList_SetItem Python Forum Discussion
Therefore, whether the function succeeds or fails, it will steal a reference count of the third argument. It must not call
Py_XDECREF/Py_DECREFin case of failure.Evidence:Reference Counting and Ownership in CPython Native API
Borrowed Reference
A borrowed reference is a reference obtained from an object that you don't own. You don't need to decrement its reference count when you're done with it, but you must ensure the object stays alive while you're using it (e.g., by creating an owned reference with
Py_INCREFif necessary). Borrowed references are typically returned by functions likePyList_GetItem(), which returns an item from a list without incrementing its reference count.New Reference
A new reference (also called an "owned reference") is a reference that you have ownership of. When you receive a new reference from a function (such as
PyObject_New()orPy_BuildValue()), you are responsible for callingPy_DECREF()on it when you no longer need it to properly decrement its reference count. Failure to do so causes memory leaks.Stolen Reference (Stealing)
A stolen reference is when a function takes ownership of a reference you pass to it. When you pass an object reference to a function that "steals" it, you no longer own that reference, and you should not call
Py_DECREF()on it afterward. The function assumes full responsibility for managing the reference count of that object.Example:
PyList_SetItemPyList_SetItemis a classic example of a reference-stealing function. According to the documentation:However, a critical ambiguity in the documentation is that it does not clearly state whether the reference is stolen in the case of function failure. This is fundamentally an all-or-nothing problem: when you pass an object to a stealing function, you must understand whether ownership is unconditionally transferred or only transferred on success.
Looking at the CPython source code clarifies this behavior:
As demonstrated in the source code above,
PyList_SetItemunconditionally callsPy_XDECREF(newitem)when the type check fails—meaning it always steals the reference, even on failure. The function takes ownership ofnewitemregardless of whether it successfully inserts the item into the list.This behavior has serious implications for correct API usage. Consider the following incorrect code:
This code is defective because it leads to a use-after-free vulnerability. Since
PyList_SetItemalready stole the reference (and decremented it on failure viaPy_XDECREF), the additionalPy_DECREF(something)in the error-handling block causes a double decrement, potentially leading to an assertion failure in debug builds or memory corruption and crashes in release builds.The correct pattern is simply:
In summary, when dealing with stealing functions in the CPython API, you must relinquish all ownership responsibility for the passed reference and never decrement it after the call, regardless of the return value. Always consult the source code or thoroughly documented behavior to confirm whether a function truly provides an all-or-nothing stealing guarantee.