From 04e95b7dd16a4f7587804c9bbb92175687a5f547 Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Fri, 27 Feb 2026 06:45:27 -0600 Subject: [PATCH] LibCore: Avoid UAF on the array of wake pipes when exit()ing If exit() is called on a thread with an EventLoop in the stack, the ThreadData storing the array of wake pipes will be destroyed first. Threads can still take a strong reference to the EventLoop after that, and will read the fds from freed memory. Instead, take a copy of the write fd, and swallow EBADF when writing to it, since that only indicates that the thread and event loop are exiting, so there's nothing to do with the wake. --- Libraries/LibCore/EventLoopImplementationUnix.cpp | 13 +++++++++++-- Libraries/LibCore/EventLoopImplementationUnix.h | 5 +++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Libraries/LibCore/EventLoopImplementationUnix.cpp b/Libraries/LibCore/EventLoopImplementationUnix.cpp index d919bc0ef0..d5ce6c6de7 100644 --- a/Libraries/LibCore/EventLoopImplementationUnix.cpp +++ b/Libraries/LibCore/EventLoopImplementationUnix.cpp @@ -263,6 +263,9 @@ struct ThreadData { ~ThreadData() { + close(wake_pipe_fds[0]); + close(wake_pipe_fds[1]); + Threading::RWLockLocker locker(s_thread_data_lock); s_thread_data.remove(s_thread_id); } @@ -286,8 +289,9 @@ struct ThreadData { } EventLoopImplementationUnix::EventLoopImplementationUnix() - : m_wake_pipe_fds(ThreadData::the().wake_pipe_fds) + : m_wake_pipe_write_fd(ThreadData::the().wake_pipe_fds[1]) { + VERIFY(m_wake_pipe_write_fd >= 0); } EventLoopImplementationUnix::~EventLoopImplementationUnix() = default; @@ -317,7 +321,12 @@ void EventLoopImplementationUnix::quit(int code) void EventLoopImplementationUnix::wake() { int wake_event = 0; - MUST(Core::System::write(m_wake_pipe_fds[1], { &wake_event, sizeof(wake_event) })); + auto result = Core::System::write(m_wake_pipe_write_fd, { &wake_event, sizeof(wake_event) }); + // EBADF here just indicates that the ThreadData is destroyed, so we must be exiting the thread. + // Ignore it. + if (result.is_error() && result.error().code() == EBADF) + return; + MUST(move(result)); } void EventLoopManagerUnix::wait_for_events(EventLoopImplementation::PumpMode mode) diff --git a/Libraries/LibCore/EventLoopImplementationUnix.h b/Libraries/LibCore/EventLoopImplementationUnix.h index 7ad53acd22..1c6dfa40c6 100644 --- a/Libraries/LibCore/EventLoopImplementationUnix.h +++ b/Libraries/LibCore/EventLoopImplementationUnix.h @@ -55,8 +55,9 @@ private: bool m_exit_requested { false }; int m_exit_code { 0 }; - // The wake pipe of this event loop needs to be accessible from other threads. - Array& m_wake_pipe_fds; + // The write end of the wake pipe, copied by value so it remains valid even + // if ThreadData is destroyed before this event loop (e.g. during exit()). + int m_wake_pipe_write_fd; }; using EventLoopManagerPlatform = EventLoopManagerUnix;