win: prevent tty event explosion machine hang

The tty subsystem on Windows was listening for console events from all
processes to detect when our console window was being resized. This
could cause an explosion in the number of events queued by the system
when running many console applications in parallel that all wrote to
their console quickly. The end result was a complete machine hang.

Now we determine, whenever possible, what our corresponding conhost.exe
process is and listen for console events from that process only. This
detection does not work in 32-bit applications running on 64-bit
Windows so those default to the old behavior of listening to all
processes.

PR-URL: https://github.com/libuv/libuv/pull/2308
Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
Reviewed-By: Bartosz Sosnowski <bartosz@janeasystems.com>
This commit is contained in:
Javier Blazquez 2019-05-21 20:23:42 -07:00 committed by Bartosz Sosnowski
parent a5d3743778
commit dabc737d78
3 changed files with 39 additions and 2 deletions

View File

@ -2280,6 +2280,8 @@ static void uv__determine_vterm_state(HANDLE handle) {
static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
CONSOLE_SCREEN_BUFFER_INFO sb_info;
NTSTATUS status;
ULONG_PTR conhost_pid;
MSG msg;
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
@ -2288,14 +2290,29 @@ static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
uv__tty_console_width = sb_info.dwSize.X;
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
if (pSetWinEventHook == NULL)
if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
return 0;
status = pNtQueryInformationProcess(GetCurrentProcess(),
ProcessConsoleHostProcess,
&conhost_pid,
sizeof(conhost_pid),
NULL);
if (!NT_SUCCESS(status))
/* We couldn't retrieve our console host process, probably because this
* is a 32-bit process running on 64-bit Windows. Fall back to receiving
* console events from all processes. */
conhost_pid = 0;
/* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
conhost_pid &= ~(ULONG_PTR)0x3;
if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
EVENT_CONSOLE_LAYOUT,
NULL,
uv__tty_console_resize_event,
0,
(DWORD)conhost_pid,
0,
WINEVENT_OUTOFCONTEXT))
return 0;

View File

@ -34,6 +34,7 @@ sNtSetInformationFile pNtSetInformationFile;
sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
sNtQueryDirectoryFile pNtQueryDirectoryFile;
sNtQuerySystemInformation pNtQuerySystemInformation;
sNtQueryInformationProcess pNtQueryInformationProcess;
/* Kernel32 function pointers */
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
@ -106,6 +107,13 @@ void uv_winapi_init(void) {
uv_fatal_error(GetLastError(), "GetProcAddress");
}
pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress(
ntdll_module,
"NtQueryInformationProcess");
if (pNtQueryInformationProcess == NULL) {
uv_fatal_error(GetLastError(), "GetProcAddress");
}
kernel32_module = GetModuleHandleA("kernel32.dll");
if (kernel32_module == NULL) {
uv_fatal_error(GetLastError(), "GetModuleHandleA");

View File

@ -4436,6 +4436,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
# define SystemProcessorPerformanceInformation 8
#endif
#ifndef ProcessConsoleHostProcess
# define ProcessConsoleHostProcess 49
#endif
#ifndef FILE_DEVICE_FILE_SYSTEM
# define FILE_DEVICE_FILE_SYSTEM 0x00000009
#endif
@ -4578,6 +4582,13 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile)
BOOLEAN RestartScan
);
typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
(HANDLE ProcessHandle,
UINT ProcessInformationClass,
PVOID ProcessInformation,
ULONG Length,
PULONG ReturnLength);
/*
* Kernel32 headers
*/
@ -4718,6 +4729,7 @@ extern sNtSetInformationFile pNtSetInformationFile;
extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile;
extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
extern sNtQuerySystemInformation pNtQuerySystemInformation;
extern sNtQueryInformationProcess pNtQueryInformationProcess;
/* Kernel32 function pointers */
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;