Subject: Fix use-after-move crash in TransferString::release() with GCC In TransferString::release(), the ExternalStringImpl::create() calls pass memory->span() as the first argument and a lambda capturing memory.releaseNonNull() as the second argument. Since C++ leaves the evaluation order of function arguments indeterminate, the compiler is free to evaluate argument 2 before argument 1. GCC typically evaluates arguments right-to-left, which means memory.releaseNonNull() runs first, nulling out the RefPtr, and then memory->span() dereferences the now-null pointer — causing a SIGSEGV ("Invalid read of size 8" in valgrind, as it tries to read the m_data and m_size members through null). Clang typically evaluates arguments left-to-right, so the bug does not manifest on macOS/Clang builds where WebKit is primarily developed and tested, which is likely why it was not caught earlier. The crash only triggers when the shared memory mapping is larger than transferAsMappingSize - 1 (16383 bytes), i.e. for IPC-transferred strings larger than ~16 KB. In the reported case, this happens when Evolution sends a long HTML email body to WebKitWebProcess via RunJavaScriptInFrameInScriptWorld. The fix extracts the span into a local variable before the ExternalStringImpl::create() call, ensuring memory->span() is always evaluated while the RefPtr is still valid. Both the SharedSpan8 (Latin1) and SharedSpan16 (char16_t) code paths are affected and fixed. References: https://bugs.gentoo.org/972453 https://bugs.webkit.org/show_bug.cgi?id=311995 https://gitlab.gnome.org/GNOME/evolution/-/work_items/3298 Assisted-by: Claude Opus 4.6 --- Source/WebKit/Platform/IPC/TransferString.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/WebKit/Platform/IPC/TransferString.cpp b/Source/WebKit/Platform/IPC/TransferString.cpp index 1248c5c7f689..088327d341c0 100644 --- a/Source/WebKit/Platform/IPC/TransferString.cpp +++ b/Source/WebKit/Platform/IPC/TransferString.cpp @@ -108,7 +108,8 @@ std::optional TransferString::release(size_t maxCopySizeInBytes) && // N if (!memory) return std::nullopt; if (memory->size() > maxCopySizeInBytes) { - Ref impl = ExternalStringImpl::create(byteCast(memory->span()), [memory = memory.releaseNonNull()] (auto...) mutable { }); + auto span = byteCast(memory->span()); + Ref impl = ExternalStringImpl::create(span, [memory = memory.releaseNonNull()] (auto...) mutable { }); return std::optional { std::in_place, String { WTF::move(impl) } }; } return std::optional { std::in_place, String { byteCast(memory->span()) } }; @@ -118,7 +119,8 @@ std::optional TransferString::release(size_t maxCopySizeInBytes) && // N if (!memory || (memory->size() % sizeof(char16_t))) return std::nullopt; if (memory->size() > maxCopySizeInBytes) { - Ref impl = ExternalStringImpl::create(spanReinterpretCast(memory->span()), [memory = memory.releaseNonNull()] (auto...) mutable { }); + auto span = spanReinterpretCast(memory->span()); + Ref impl = ExternalStringImpl::create(span, [memory = memory.releaseNonNull()] (auto...) mutable { }); return std::optional { std::in_place, String { WTF::move(impl) } }; } return std::optional { std::in_place, String { spanReinterpretCast(memory->span()) } }; -- 2.49.0