-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
select.kqueue uses invalid fd after fork #110395
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
Comments
import os def safe_kqueue():
kq = safe_kqueue() Continue using kq in the parent process |
@Nau56 unfortunately, calling |
This comment was marked as spam.
This comment was marked as spam.
Gave it a try in #110517. Personally I find it a bit unsavory to call |
Oh, that's bad. But should Python really attempt to be nice to every crazy use cases? Can't users take care of that themselves? @gpshead: Should we be extra nice to users executing non-trivial code after fork()? What's the trend in Linux, do you we also do that? |
I think this is something that can be fixed and the proposed PR makes sense. There are still people using non-threaded forking-server-stack style applications. This would apply to those, on platforms with kqueue (FreeBSD, macOS?, etc). |
As for calling the Python |
If you have a process with an open kqueue and it forks-without-exec you have no way to take care of that in user code, though. That was my rationale for the fix. Whether it is a "crazy" use case I can't comment. But forking-without-exec is still supported.
Yeah, I went through the same thought process. It helped that there is no precedent for using |
Invalidate open select.kqueue instances after fork as the fd will be invalid in the child.
…110517) Invalidate open select.kqueue instances after fork as the fd will be invalid in the child. (cherry picked from commit a6c1c04) Co-authored-by: Davide Rizzo <[email protected]>
…110517) Invalidate open select.kqueue instances after fork as the fd will be invalid in the child. (cherry picked from commit a6c1c04) Co-authored-by: Davide Rizzo <[email protected]>
based on review from Victor Stinner.
based on review from Victor Stinner. I already made this edit in the 3.12 backport PR.
based on review from Victor Stinner. I already made this edit in the 3.12 backport PR.
…1745) * [3.12] gh-110395: invalidate open kqueues after fork (GH-110517) Invalidate open select.kqueue instances after fork as the fd will be invalid in the child. (cherry picked from commit a6c1c04) Co-authored-by: Davide Rizzo <[email protected]> * move assert to after the child dying this is in `main` via https://github.com/python/cpython/pull/111816/files
thanks! |
Invalidate open select.kqueue instances after fork as the fd will be invalid in the child.
based on review from Victor Stinner. I already made this edit in the 3.12 backport PR.
Invalidate open select.kqueue instances after fork as the fd will be invalid in the child.
based on review from Victor Stinner. I already made this edit in the 3.12 backport PR.
Bug report
Bug description:
select.kqueue
is not aware of forks, and will try to use its file descriptor number after fork. Since kqueues are not inherited1 in children, the fd will be invalid, or it can refer to a file opened by someone else.Reproducer for a case where
select.kqueue
will close the fd opened withopen()
on its destructor:Reproducer with asyncio
This will fail with
OSError: [Errno 9] Bad file descriptor
when operating on f, because its fd was coincidentally closed by the loop destructor. Dropping the reference after fork does not help; it actually makes the problem worse, because the loop becomes cyclic garbage and the kqueue can be closed at a later, less predictable time.In the asyncio example I need a bit of setup: the first loop object needs to be open at fork time2. The bug will be observable if, in the child, the loop's kqueue is closed/destructed after a different fd is opened.
I encountered this because I got
test_asyncio.test_unix_events.TestFork
failures while working on something unrelated (and it's been a pain to debug), not in production code. I guess it can still happen in real-world code though, because there is no proper way to dispose of a select.kqueue object in a forked process, and it's hard to debug a random EBADF that triggers only on Mac/BSD in otherwise-correct code.I'm willing to work on a fix, if one is desired, but I'll need some guidance on the strategy. I thought
select.kqueue
objects can be invalidated after fork, but that would add some (small) tracking overhead so I don't know if it's acceptable.CPython versions tested on:
3.11, 3.12, CPython main branch
Operating systems tested on:
macOS, Other
Linked PRs
Footnotes
the queue itself is not available in children, and the OS will automatically close any fd referring to a kqueue after fork. ↩
this can also happen if event loop policy holds a reference to a loop, and later is dropped (maybe to create a new loop in the child). Or a Runner context is still active at fork time and is exited in the child. ↩
The text was updated successfully, but these errors were encountered: