-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use-after-free in dict/list #82769
Comments
Code : The varanit bval forget call Py_INCREF to add reference in dict_equal() b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); <--- ...
if (bval == NULL) {
Py_DECREF(key);
Py_DECREF(aval);
if (PyErr_Occurred())
return -1;
return 0;
}
cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); PoC 1 : class poc() :
def __eq__(self,other) :
dict2.clear()
return NotImplemented
dict1 = {0:poc()}
dict2 = {0:set()}
dict1 == dict2 ## dict_equal() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/dict_poc_1.py Program received signal SIGSEGV, Segmentation fault. ====== Code : The varanit wl->ob_item[i] forget call Py_INCREF to add reference in list_richcompare() for (i = 0; i < Py_SIZE(vl) && i < Py_SIZE(wl); i++) {
int k = PyObject_RichCompareBool(vl->ob_item[i],
wl->ob_item[i], Py_EQ); < PoC 2 : class poc() :
def __eq__(self,other) :
list1.clear()
return NotImplemented
list1 = [poc()]
list2 = [1]
list1 == list2 # list_richcompare() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/list_poc_1.py Program received signal SIGSEGV, Segmentation fault. ====== Code : The varanit PyList_GET_ITEM(a, i) forget call Py_INCREF to add reference in list_contains() list_contains(PyListObject *a, PyObject *el)
{
Py_ssize_t i;
int cmp;
PoC 3 : class poc() :
def __eq__(self,other) :
list1.clear()
return NotImplemented
list1 = [ set() ]
poc() in list1 # list_contains() -> PyObject_RichCompareBool Crash Detail : (gdb) run ../python_poc_info/list_poc_2.py Program received signal SIGSEGV, Segmentation fault. |
Thank you for your investigation LCatro! Do you mind to create a pull request? |
Sure ,but how can i pull my fix code ? |
Would you benchmark the performance? How about calling Py_INCREF and Py_DECREF in PyObject_RichCompare or do_richcompare? It is safer than checking all caller of the PyObject_RichCompare and PyObject_RichCompareBool. /* Quick result when objects are the same.
Guarantees that identity implies equality. */
if (v == w) {
if (op == Py_EQ)
return 1;
else if (op == Py_NE)
return 0;
} |
If we can not add INCREF and DECREF in the PyObject_RichCompare, we can add v == w check in the caller side. |
Apologies, I had missed this suggestion before merging the PR :( If we decide to add the check to PyObject_RichCompare or do_richcompare we should also adapt the fix for https://bugs.python.org/issue38610 |
$ ./python -m pyperf timeit -s 'a = ["a"]*100; b = ["a"]*100;' -- 'a == b' master : Mean +- std dev: 276 ns +- 1 ns This makes list comparison 2x slower. |
Would you like to revert PR 17734? Calling Py_INCREF and Py_DECREF in PyObject_RichCompare or do_richcompare will take the same effect, no? |
This is affected by PR 17734? or PyObject_RichCompare patched? |
Master Before PR-17734 New suggested diff --git a/Objects/object.c b/Objects/object.c
index 6fc1146..b42f41a 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -865,6 +865,8 @@ PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
return 0;
} + Py_INCREF(v);
+ Py_INCREF(w);
res = PyObject_RichCompare(v, w, op);
if (res == NULL)
return -1;
@@ -873,6 +875,8 @@ PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
else
ok = PyObject_IsTrue(res);
Py_DECREF(res);
+ Py_DECREF(v);
+ Py_DECREF(w);
return ok;
} |
Caused by PR 17734.
Moving INCREF and DECREF is a huge change. It is just a future idea to prevent same type of bugs. I think it can not be backported. To fix this issue with minimum performance regression, I think PR 17766 is the best solution. So no need to revert PR 17734. |
Now I am wondering how many other APIs are affected by the same pattern other than PyObject_RichCompareBool....
Thanks for the quick fix and the analysis. I reviewed PR 17734. |
Sorry, I meant that I reviewed PR 17766. |
Closing this for now, let's open another issue if we plan to discuss calling Py_INCREF and Py_DECREF in PyObject_RichCompare or do_richcompare in the future. Thanks to everyone involved! |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: