From dabc737d7892971289537e52af4366c63cbc70b3 Mon Sep 17 00:00:00 2001 From: Javier Blazquez Date: Tue, 21 May 2019 20:23:42 -0700 Subject: [PATCH] win: prevent tty event explosion machine hang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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é Reviewed-By: Bartosz Sosnowski --- src/win/tty.c | 21 +++++++++++++++++++-- src/win/winapi.c | 8 ++++++++ src/win/winapi.h | 12 ++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/win/tty.c b/src/win/tty.c index a98fe263..07436dc8 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -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; diff --git a/src/win/winapi.c b/src/win/winapi.c index fbbbceed..19e4377f 100644 --- a/src/win/winapi.c +++ b/src/win/winapi.c @@ -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"); diff --git a/src/win/winapi.h b/src/win/winapi.h index 82c5ed46..203393c2 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -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;