-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
bpo-30555: Fix WindowsConsoleIO errors in the presence of fd redirection #1927
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
bpo-30555: Fix WindowsConsoleIO errors in the presence of fd redirection #1927
Conversation
@segevfiner, thanks for your PR! By analyzing the history of the files in this pull request, we identified @zooba, @benjaminp and @serhiy-storchaka to be potential reviewers. |
Most of the reference leaks are due to initializing Some of the reported ref leaks are from calling
|
Modules/_io/winconsoleio.c
Outdated
@@ -133,6 +133,21 @@ char _PyIO_get_console_type(PyObject *path_or_fd) { | |||
return m; | |||
} | |||
|
|||
static HANDLE py_get_osfhandle(int fd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_Py_get_osfhandle
, _Py_get_osfhandle_noraise
, _Py_open_osfhandle
, and _Py_open_osfhandle_noraise
could be added in Python/fileutils.c. These CRT functions are called in fileutils.c, msvcrtmodule.c, posixmodule.c, mmapmodule.c, myreadline.c, _testconsole.c, and here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
Do you wish me to:
- Do this entire refactor right here in this PR.
- Add the functions and only use them in winconsoleio.c and _testconsole.c which I touched in this PR and leave the rest of the modules to a separate PR.
- Leave this to a separate PR entirely.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented this, with the full replacement in a separate commit just in case it's frowned upon by a core developer during review.
@eryksun So the leaks are real than, nice find! 😄 I can submit a PR/issue with your suggested fix if it helps you or just leave it to you. Why would calling EDIT: I can also apply your fix right here if you wish me to. |
It should be ok to fix the |
…tion This works by not caching the handle and instead getting the handle from the file descriptor each time, so that if the actual handle changes by fd redirection closing/opening the console handle beneath our feet, we will keep working correctly. I think I also fixed some resource handling issues along the way since ucrt/msvcrt fds *own* the HANDLEs they use and this module tried to close them itself.
The change in test_winconsoleio.py is only to avoid the ref leak in `sys.getwindowsversion`.
ac3af9e
to
aaf6020
Compare
Include/fileutils.h
Outdated
@@ -117,7 +117,15 @@ PyAPI_FUNC(int) _Py_dup(int fd); | |||
PyAPI_FUNC(int) _Py_get_blocking(int fd); | |||
|
|||
PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); | |||
#endif /* !MS_WINDOWS */ | |||
#else /* MS_WINDOWS */ | |||
PyAPI_FUNC(intptr_t) _Py_get_osfhandle_noraise(int fd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought one of benefits would be using HANDLE
in the signature instead of intptr_t
, to avoid all of the casts. I guess we can't. How about void *
like _PyOS_SigintEvent
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think CPython explicity doesn't include Windows.h anywhere in it's public headers. fileutils.h is included by Python.h so I don't think I can really include Windows.h there. Ae such I don't have HANDLE defined and assuming that someone included Windows.h by himself before this header breaks horribly of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HANDLE
is a typedef
for void *
, so we can use void *
instead, like _PyOS_SigintEvent
does.
50ff023
to
7727bc5
Compare
PC/msvcrtmodule.c
Outdated
@@ -167,15 +167,7 @@ static long | |||
msvcrt_open_osfhandle_impl(PyObject *module, intptr_t handle, int flags) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The clinic definition could be updated here, e.g. to use a HANDLE
type based on void *
:
class HANDLE_converter(CConverter):
type = 'void *'
format_unit = '"_Py_PARSE_INTPTR"'
class HANDLE_return_converter(CReturnConverter):
type = 'void *'
def render(self, function, data):
self.declare(data)
self.err_occurred_if("_return_value == INVALID_HANDLE_VALUE", data)
data.return_conversion.append(
'return_value = PyLong_FromVoidPtr(_return_value);\n')
Then change the open_osfhandle
parameter to handle: HANDLE
and the get_osfhandle
return type to -> HANDLE
.
…consoleio-fd-redirection
…consoleio-fd-redirection
Merged from master due to argument clinic changes. |
…consoleio-fd-redirection
…consoleio-fd-redirection
…consoleio-fd-redirection
c3953d5
to
2d252ab
Compare
…consoleio-fd-redirection
Co-authored-by: Eryk Sun <eryksun@gmail.com>
Thanks for persisting with this, and sorry for taking so long! Let's put it in and deal with any fallout in 3.10 before backporting. |
Thanks @segevfiner for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.9. |
Thanks @segevfiner for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.8. |
Sorry, @segevfiner and @zooba, I could not cleanly backport this to |
Sorry @segevfiner and @zooba, I had trouble checking out the |
Thanks @segevfiner for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.8. |
Thanks @segevfiner for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.9. |
Sorry, @segevfiner and @zooba, I could not cleanly backport this to |
Sorry @segevfiner and @zooba, I had trouble checking out the |
This works by not caching the handle and instead getting the handle from the file descriptor each time, so that if the actual handle changes by fd redirection closing/opening the console handle beneath our feet, we will keep working correctly.
I think I also fixed some resource handling issues along the way since ucrt/msvcrt fds own the HANDLEs they use and this module tried to close them itself.
See pytest-dev/py#103 for the issue it causes in Pytest. Also see: pytest-dev/pytest#2462. I was able to run the Pytest test suite, and my own little test described in the PR, to completion after this change.
Having your console handle closed by fd redirection is likely a change in the ucrt/msvcrt or a change in newer Windows versions (Console handles were rewritten in Windows 8).
This likely also affects any other module caching
GetStdHandle
or_get_osfhandle
. For example colorama (pytest-dev/pytest#2465 - tartley/colorama#131), and I can think of "pyreadline", "win-unicode-console" and "click" probably suffering from the same issue but I haven't tested them.I'm also not sure of the performance implications of this change.
This affects Python 3.6-3.7
P.S.
python -m test -R 3:2 test.test_winconsoleio
reports ref leaks on master for me 😛CC @zooba @eryksun