-
-
Notifications
You must be signed in to change notification settings - Fork 32k
UAF in asyncio.Future
when removing a callback while fut->fut_callbacks
length is 1
#126405
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
Labels
3.12
only security fixes
3.13
bugs and security fixes
3.14
bugs and security fixes
extension-modules
C modules in the Modules dir
topic-asyncio
type-crash
A hard crash of the interpreter, possibly with a core dump
Comments
Thank you! I will take a look. |
Hm, this code works without any crashes on import asyncio
async def main():
fut = asyncio.Future()
class ConfirmUAF:
def __eq__(self, other):
print("!!!! How did we get here !!!!")
class Base:
def __eq__(self, other):
print("in tracker eq", self, other)
return other != pad
def __del__(self):
# to see when objects are being deleted
print("deleting", self)
class Evil(Base):
def __eq__(self, other):
global _no_del
print("in evil eq", self, other)
fut.remove_done_callback(Base())
old_id = id(other)
del other
_no_del = ConfirmUAF()
new_id = id(_no_del)
# if these two are the same, you'll end up in the ConfirmUAF.__eq__ func
# if not, you'll probably just crash
print(f"{old_id = :#x} {new_id = :#x}")
return NotImplemented
pad = ...
fut.add_done_callback(pad)
fut.add_done_callback(Base())
assert fut.remove_done_callback(pad) == 1
print("starting bug")
assert fut.remove_done_callback(Evil()) == 0
asyncio.run(main()) |
The original PoC doesnt crash because I fill the spot where the freed memory is with the class Evil(Base):
def __eq__(self, other):
global _no_del
print("in evil eq", self, other)
fut.remove_done_callback(Base())
# old_id = id(other)
del other
# _no_del = ConfirmUAF()
# new_id = id(_no_del)
# if these two are the same, you'll end up in the ConfirmUAF.__eq__ func
# if not, you'll probably just crash
# print(f"{old_id = :#x} {new_id = :#x}")
return NotImplemented |
kumaraditya303
added a commit
that referenced
this issue
Nov 12, 2024
miss-islington
pushed a commit
to miss-islington/cpython
that referenced
this issue
Nov 12, 2024
…allback` (pythonGH-126733) (cherry picked from commit 37c57df) Co-authored-by: Kumar Aditya <[email protected]>
miss-islington
pushed a commit
to miss-islington/cpython
that referenced
this issue
Nov 12, 2024
…allback` (pythonGH-126733) (cherry picked from commit 37c57df) Co-authored-by: Kumar Aditya <[email protected]>
This was referenced Nov 12, 2024
Merged
picnixz
pushed a commit
to picnixz/cpython
that referenced
this issue
Dec 8, 2024
ebonnal
pushed a commit
to ebonnal/cpython
that referenced
this issue
Jan 12, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
3.12
only security fixes
3.13
bugs and security fixes
3.14
bugs and security fixes
extension-modules
C modules in the Modules dir
topic-asyncio
type-crash
A hard crash of the interpreter, possibly with a core dump
Crash report
What happened?
This bug boils down to a missing incref on the callback before calling
PyObject_RichCompareBool
. Although we can't directly control the callbacks list withfut._callbacks
ever since it was made to return a copy every time, we can still modify it by removing/adding callbacks in evil__eq__
functions.The bug is here
cpython/Modules/_asynciomodule.c
Lines 1049 to 1052 in 3032fcd
PoC
Output
CPython versions tested on:
3.13, 3.14
Operating systems tested on:
Linux, Windows
Output from running 'python -VV' on the command line:
Python 3.14.0a1+ (heads/main:85799f1ffd, Nov 4 2024, 11:28:25) [GCC 13.2.0]
Linked PRs
_asyncio.Future.remove_done_callback
#126733_asyncio.Future.remove_done_callback
(GH-126733) #126736_asyncio.Future.remove_done_callback
(GH-126733) #126737The text was updated successfully, but these errors were encountered: