[Lldb-commits] [lldb] [lldb][windows] prevent IOHandlerProcessSTDIOWindows from consuming non text inputs (PR #175812)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue Jan 20 06:49:26 PST 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/175812
>From ca113a51b42d08382e0a9b6dd0636eba9f0d6ab4 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 13 Jan 2026 18:54:43 +0000
Subject: [PATCH 1/8] [lldb][windows] prevent IOHandlerProcessSTDIOWindows from
consuming non text inputs
---
.../Process/Windows/Common/ProcessWindows.cpp | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 127dd0f59e9ae..72043d16313cd 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -981,6 +981,9 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
HANDLE hInterrupt = (HANDLE)_get_osfhandle(m_pipe.GetReadFileDescriptor());
HANDLE waitHandles[2] = {hStdin, hInterrupt};
+ DWORD consoleMode;
+ bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
+
while (true) {
{
std::lock_guard<std::mutex> guard(m_mutex);
@@ -993,6 +996,20 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
case WAIT_FAILED:
goto exit_loop;
case WAIT_OBJECT_0: {
+ if (isConsole) {
+ INPUT_RECORD inputRecord;
+ DWORD numRead = 0;
+ if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
+ numRead == 0)
+ goto exit_loop;
+ // We only care about text input. Ignore all non text input events and
+ // let other IOHandlers handle them.
+ if (inputRecord.EventType != KEY_EVENT ||
+ !inputRecord.Event.KeyEvent.bKeyDown ||
+ inputRecord.Event.KeyEvent.uChar.AsciiChar == 0)
+ break;
+ }
+
char ch = 0;
DWORD read = 0;
if (!ReadFile(hStdin, &ch, 1, &read, nullptr) || read != 1)
>From 0fa56fa8a08ea20f031baddce32d85f4bef20a41 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 19 Jan 2026 13:50:09 +0000
Subject: [PATCH 2/8] consume non text input
---
.../Process/Windows/Common/ProcessWindows.cpp | 28 +++++++++++--------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 72043d16313cd..ed02f243a5445 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -997,17 +997,23 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
goto exit_loop;
case WAIT_OBJECT_0: {
if (isConsole) {
- INPUT_RECORD inputRecord;
- DWORD numRead = 0;
- if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
- numRead == 0)
- goto exit_loop;
- // We only care about text input. Ignore all non text input events and
- // let other IOHandlers handle them.
- if (inputRecord.EventType != KEY_EVENT ||
- !inputRecord.Event.KeyEvent.bKeyDown ||
- inputRecord.Event.KeyEvent.uChar.AsciiChar == 0)
- break;
+ while (true) {
+ INPUT_RECORD inputRecord;
+ DWORD numRead = 0;
+ if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
+ numRead == 0)
+ goto exit_loop;
+
+ // We only care about text input. Consume all non text input events before letting ReadFile handle text input.
+ if (inputRecord.EventType == KEY_EVENT &&
+ inputRecord.Event.KeyEvent.bKeyDown &&
+ inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
+ break;
+
+ if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
+ numRead == 0)
+ goto exit_loop;
+ }
}
char ch = 0;
>From 275e21f2179b313337d56f0ada62f6fb70dd767d Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 19 Jan 2026 13:57:50 +0000
Subject: [PATCH 3/8] fix formatting
---
lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index ed02f243a5445..0717f4f50a02a 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -1004,7 +1004,8 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
numRead == 0)
goto exit_loop;
- // We only care about text input. Consume all non text input events before letting ReadFile handle text input.
+ // We only care about text input. Consume all non text input events
+ // before letting ReadFile handle text input.
if (inputRecord.EventType == KEY_EVENT &&
inputRecord.Event.KeyEvent.bKeyDown &&
inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
>From 4a6e01771e9c7b3081dfa8f6d4b69e01e5397e98 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 19 Jan 2026 15:50:09 +0000
Subject: [PATCH 4/8] fix control flow
---
lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 0717f4f50a02a..2afb654775855 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -1000,8 +1000,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
while (true) {
INPUT_RECORD inputRecord;
DWORD numRead = 0;
- if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
- numRead == 0)
+ if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
goto exit_loop;
// We only care about text input. Consume all non text input events
>From d2b6d061b3f4dee66a89af551ca5f69bbd1669ef Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Mon, 19 Jan 2026 17:32:34 +0000
Subject: [PATCH 5/8] fix control flow
---
.../Plugins/Process/Windows/Common/ProcessWindows.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 2afb654775855..5847d47126b33 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -985,6 +985,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
while (true) {
+ read_loop:;
{
std::lock_guard<std::mutex> guard(m_mutex);
if (GetIsDone())
@@ -1003,6 +1004,9 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
goto exit_loop;
+ if (numRead == 0)
+ goto read_loop;
+
// We only care about text input. Consume all non text input events
// before letting ReadFile handle text input.
if (inputRecord.EventType == KEY_EVENT &&
@@ -1010,8 +1014,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
break;
- if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead) ||
- numRead == 0)
+ if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead))
goto exit_loop;
}
}
@@ -1045,7 +1048,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
}
exit_loop:;
- SetIsRunning(false);
+ SetIsRunning(false);
}
void Cancel() override {
>From b158f2de731697f2bc8f0b5a55e5e42d621acd1f Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 20 Jan 2026 11:15:27 +0000
Subject: [PATCH 6/8] add comment
---
lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 5847d47126b33..e73e86f745c0b 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -1004,6 +1004,8 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
goto exit_loop;
+ // If the pipe is empty, go back to waiting on
+ // WaitForMultipleObjects rather than ReadFile.
if (numRead == 0)
goto read_loop;
@@ -1048,7 +1050,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
}
exit_loop:;
- SetIsRunning(false);
+ SetIsRunning(false);
}
void Cancel() override {
>From 56e87c072141a8ac8a15a1bb434027ea84d2aa34 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 20 Jan 2026 14:33:32 +0000
Subject: [PATCH 7/8] remove secondary pipe
---
.../Process/Windows/Common/ProcessWindows.cpp | 109 ++++++++++--------
1 file changed, 58 insertions(+), 51 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index e73e86f745c0b..50917fdb336b5 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -956,7 +956,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
m_process(process),
m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
m_write_file(conpty_input) {
- m_pipe.CreateNew();
+ m_interrupt_event = INVALID_HANDLE_VALUE;
}
~IOHandlerProcessSTDIOWindows() override = default;
@@ -967,9 +967,36 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
m_is_running = running;
}
+ /// Peek the console for input. If it has any, drain the pipe until text input
+ /// is found or the pipe is empty.
+ ///
+ /// \param[in, out] hStdin
+ /// The handle to the standard input's pipe.
+ ///
+ /// \return
+ /// true if the pipe has text input.
+ llvm::Expected<bool> ConsoleHasTextInput(HANDLE hStdin) {
+ while (true) {
+ INPUT_RECORD inputRecord;
+ DWORD numRead = 0;
+ if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
+ return llvm::createStringError("Failed to peek standard input.");
+
+ if (numRead == 0)
+ return false;
+
+ if (inputRecord.EventType == KEY_EVENT &&
+ inputRecord.Event.KeyEvent.bKeyDown &&
+ inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
+ return true;
+
+ if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead))
+ return llvm::createStringError("Failed to read standard input.");
+ }
+ }
+
void Run() override {
- if (!m_read_file.IsValid() || m_write_file == INVALID_HANDLE_VALUE ||
- !m_pipe.CanRead() || !m_pipe.CanWrite()) {
+ if (!m_read_file.IsValid() || m_write_file == INVALID_HANDLE_VALUE) {
SetIsDone(true);
return;
}
@@ -977,15 +1004,13 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
SetIsDone(false);
SetIsRunning(true);
- HANDLE hStdin = (HANDLE)_get_osfhandle(m_read_file.GetDescriptor());
- HANDLE hInterrupt = (HANDLE)_get_osfhandle(m_pipe.GetReadFileDescriptor());
- HANDLE waitHandles[2] = {hStdin, hInterrupt};
+ HANDLE hStdin = m_read_file.GetWaitableHandle();
+ HANDLE waitHandles[2] = {hStdin, m_interrupt_event};
DWORD consoleMode;
bool isConsole = GetConsoleMode(hStdin, &consoleMode) != 0;
while (true) {
- read_loop:;
{
std::lock_guard<std::mutex> guard(m_mutex);
if (GetIsDone())
@@ -998,27 +1023,13 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
goto exit_loop;
case WAIT_OBJECT_0: {
if (isConsole) {
- while (true) {
- INPUT_RECORD inputRecord;
- DWORD numRead = 0;
- if (!PeekConsoleInput(hStdin, &inputRecord, 1, &numRead))
- goto exit_loop;
-
- // If the pipe is empty, go back to waiting on
- // WaitForMultipleObjects rather than ReadFile.
- if (numRead == 0)
- goto read_loop;
-
- // We only care about text input. Consume all non text input events
- // before letting ReadFile handle text input.
- if (inputRecord.EventType == KEY_EVENT &&
- inputRecord.Event.KeyEvent.bKeyDown &&
- inputRecord.Event.KeyEvent.uChar.AsciiChar != 0)
- break;
-
- if (!ReadConsoleInput(hStdin, &inputRecord, 1, &numRead))
- goto exit_loop;
- }
+ auto hasInputOrErr = ConsoleHasTextInput(hStdin);
+ if (hasInputOrErr.takeError())
+ goto exit_loop;
+
+ // If no text input is ready, go back to waiting.
+ if (!*hasInputOrErr)
+ continue;
}
char ch = 0;
@@ -1032,14 +1043,10 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
break;
}
case WAIT_OBJECT_0 + 1: {
- char ch = 0;
- DWORD read = 0;
- if (!ReadFile(hInterrupt, &ch, 1, &read, nullptr) || read != 1)
+ ControlOp op = m_pending_op.exchange(eControlOpNone);
+ if (op == eControlOpQuit)
goto exit_loop;
-
- if (ch == eControlOpQuit)
- goto exit_loop;
- if (ch == eControlOpInterrupt &&
+ if (op == eControlOpInterrupt &&
StateIsRunningState(m_process->GetState()))
m_process->SendAsyncInterrupt();
break;
@@ -1057,18 +1064,16 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
std::lock_guard<std::mutex> guard(m_mutex);
SetIsDone(true);
if (m_is_running) {
- char ch = eControlOpQuit;
- if (llvm::Error err = m_pipe.Write(&ch, 1).takeError()) {
- LLDB_LOG_ERROR(GetLog(LLDBLog::Process), std::move(err),
- "Pipe write failed: {0}");
- }
+ m_pending_op.store(eControlOpQuit);
+ ::SetEvent(m_interrupt_event);
}
}
bool Interrupt() override {
if (m_active) {
- char ch = eControlOpInterrupt;
- return !errorToBool(m_pipe.Write(&ch, 1).takeError());
+ m_pending_op.store(eControlOpInterrupt);
+ ::SetEvent(m_interrupt_event);
+ return true;
}
if (StateIsRunningState(m_process->GetState())) {
m_process->SendAsyncInterrupt();
@@ -1080,19 +1085,21 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
void GotEOF() override {}
private:
- Process *m_process;
- NativeFile m_read_file; // Read from this file (usually actual STDIN for LLDB
- HANDLE m_write_file =
- INVALID_HANDLE_VALUE; // Write to this file (usually the primary pty for
- // getting io to debuggee)
- Pipe m_pipe;
- std::mutex m_mutex;
- bool m_is_running = false;
-
enum ControlOp : char {
eControlOpQuit = 'q',
eControlOpInterrupt = 'i',
+ eControlOpNone = 0,
};
+
+ Process *m_process;
+ /// Read from this file (usually actual STDIN for LLDB)
+ NativeFile m_read_file;
+ /// Write to this file (usually the primary pty for getting io to debuggee)
+ HANDLE m_write_file = INVALID_HANDLE_VALUE;
+ HANDLE m_interrupt_event = INVALID_HANDLE_VALUE;
+ std::atomic<ControlOp> m_pending_op{eControlOpNone};
+ std::mutex m_mutex;
+ bool m_is_running = false;
};
void ProcessWindows::SetPseudoConsoleHandle(
>From ea41bc7ca9f2820182c262d856d5f14a4f281d93 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 20 Jan 2026 14:49:09 +0000
Subject: [PATCH 8/8] edit signature
---
.../Plugins/Process/Windows/Common/ProcessWindows.cpp | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 50917fdb336b5..efaa893fc62d8 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -955,9 +955,7 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
IOHandler::Type::ProcessIO),
m_process(process),
m_read_file(GetInputFD(), File::eOpenOptionReadOnly, false),
- m_write_file(conpty_input) {
- m_interrupt_event = INVALID_HANDLE_VALUE;
- }
+ m_write_file(conpty_input), m_interrupt_event(INVALID_HANDLE_VALUE) {}
~IOHandlerProcessSTDIOWindows() override = default;
@@ -970,12 +968,12 @@ class IOHandlerProcessSTDIOWindows : public IOHandler {
/// Peek the console for input. If it has any, drain the pipe until text input
/// is found or the pipe is empty.
///
- /// \param[in, out] hStdin
+ /// \param hStdin
/// The handle to the standard input's pipe.
///
/// \return
/// true if the pipe has text input.
- llvm::Expected<bool> ConsoleHasTextInput(HANDLE hStdin) {
+ llvm::Expected<bool> ConsoleHasTextInput(const HANDLE hStdin) {
while (true) {
INPUT_RECORD inputRecord;
DWORD numRead = 0;
More information about the lldb-commits
mailing list