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 <noreply@anthropic.com>

---
 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<String> TransferString::release(size_t maxCopySizeInBytes) && // N
             if (!memory)
                 return std::nullopt;
             if (memory->size() > maxCopySizeInBytes) {
-                Ref<StringImpl> impl = ExternalStringImpl::create(byteCast<Latin1Character>(memory->span()), [memory = memory.releaseNonNull()] (auto...) mutable { });
+                auto span = byteCast<Latin1Character>(memory->span());
+                Ref<StringImpl> impl = ExternalStringImpl::create(span, [memory = memory.releaseNonNull()] (auto...) mutable { });
                 return std::optional<String> { std::in_place, String { WTF::move(impl) } };
             }
             return std::optional<String> { std::in_place, String { byteCast<Latin1Character>(memory->span()) } };
@@ -118,7 +119,8 @@ std::optional<String> TransferString::release(size_t maxCopySizeInBytes) && // N
             if (!memory || (memory->size() % sizeof(char16_t)))
                 return std::nullopt;
             if (memory->size() > maxCopySizeInBytes) {
-                Ref<StringImpl> impl = ExternalStringImpl::create(spanReinterpretCast<const char16_t>(memory->span()), [memory = memory.releaseNonNull()] (auto...) mutable { });
+                auto span = spanReinterpretCast<const char16_t>(memory->span());
+                Ref<StringImpl> impl = ExternalStringImpl::create(span, [memory = memory.releaseNonNull()] (auto...) mutable { });
                 return std::optional<String> { std::in_place, String { WTF::move(impl) } };
             }
             return std::optional<String> { std::in_place, String { spanReinterpretCast<const char16_t>(memory->span()) } };
--
2.49.0

