← Back to microsoft/playwright
microsoft / playwright · Issue No. 42803
Playwright's WebKit download bundles libsoup 3.6.5, which contains a session
heap-use-after-free fixed upstream in libsoup 3.6.6. On Linux it killsWPENetworkProcess under ordinary test load — several pages holding WebSockets
while another page navigates and cancels its in-flight subresources — and the
test sees Network process crashed on the socket pluspage.goto: WebKit encountered an internal error.
The bundled version is unchanged across releases, so upgrading Playwright does
not help:
| Playwright | WebKit build | bundled libsoup |
|---|---|---|
| 1.62.1 | webkit-2336 (26.5) | 3.6.5 |
| 1.63.0 | webkit-2359 | 3.6.5 |
| 1.64.0-alpha-2026-09-19 | webkit-2365 (26.6) | 3.6.5 |
$ strings -a ~/.cache/ms-playwright/webkit-2336/minibrowser-wpe/sys/lib/libsoup-3.0.so.0 | grep -E '^libsoup/3'
libsoup/3.6.5
libsoup 3.6.5 can finish one SoupMessageQueueItem twice when an async
request's read ends in an error: run_until_read_done() re-entersSOUP_MESSAGE_FINISHING on an item that the I/O completion already moved toSOUP_MESSAGE_FINISHED. The second unqueue releases each queuedSoupSessionFeature again, which consumes the session's own reference, andpriv->features keeps a list node pointing at freed memory. The next request
walks that list in soup_session_append_queue_item():
for (f = priv->features; f; f = g_slist_next (f)) {
SoupSessionFeature *feature = SOUP_SESSION_FEATURE (f->data);
g_object_ref (feature); /* <-- freed object */
soup_session_feature_request_queued (feature, msg);
}
Upstream fix: 794e089
— "fix 'heap-use-after-free' caused by 'finishing' queue item twice", released
in 3.6.6. The same release also carries0f424631,
which stops the unqueue walking the session's current feature list instead of
the one the item queued against — a second route to the same stale node.
Two cores, captured from the bundled WPENetworkProcess (webkit-2336, Ubuntu
26.04 arm64), both while a Playwright-driven page load was in flight.
SIGSEGV, faulting address 0x1 — the freed feature; the frame is exactly
the loop quoted above:
Program terminated with signal SIGSEGV, Segmentation fault.
#0 g_type_check_instance_is_fundamentally_a () from /usr/lib/aarch64-linux-gnu/libgobject-2.0.so.0
#1 g_object_ref () from /usr/lib/aarch64-linux-gnu/libgobject-2.0.so.0
#2 ?? () from .../webkit-2336/minibrowser-wpe/sys/lib/libsoup-3.0.so.0
#3 soup_session_send_async () from .../webkit-2336/minibrowser-wpe/sys/lib/libsoup-3.0.so.0
#4 ?? () from .../webkit-2336/minibrowser-wpe/lib/libWPEWebKit-2.0.so.1
Disassembly at the fault: x23 = priv->features; x22 = x23->data; g_object_ref(x22), and the object's class pointer is 0x1. The GSList node
itself is intact — only the object it names is gone.
SIGABRT, the same corruption detected later, in an unrelated allocation:
#6 malloc_printerr (str=0x... "malloc(): unaligned tcache chunk detected")
#9 __libc_calloc ()
#10 g_malloc0 () from /usr/lib/aarch64-linux-gnu/libglib-2.0.so.0
#11 g_source_new () from /usr/lib/aarch64-linux-gnu/libglib-2.0.so.0
#12 ?? () from .../minibrowser-wpe/sys/lib/libsoup-3.0.so.0
With webkit.launch() against a local https origin (a Next.js dev server, so
every page holds an HMR WebSocket): three pages loaded and left alone, a fourth
page looping goto('/other', {waitUntil: 'commit'}) / goto('/', {waitUntil: 'commit'}) so each navigation cancels the previous document's still-loading
chunks. The network process died after 4, 21, 24 and 37 rounds across runs
on a t4g.xlarge (Ubuntu 26.04 arm64, GLib 2.88).
I could not reduce this to a dependency-free script: a hand-rolled server with
slow chunks, a WebSocket and aborted fetches ran 400 rounds clean, and the same
shape in an Ubuntu 26.04 container with the identical WebKit bytes ran 2000
rounds clean. Timing decides whether the freed slot is reused, which is what a
use-after-free looks like from outside. The version fact and the two cores are
the parts that do not depend on my host.
Identical probe, same host, only the library changed — the distro's libsoup
3.6.6 loaded in front of the bundled 3.6.5 (LD_PRELOAD; the bundle'sMiniBrowser wrapper overwrites LD_LIBRARY_PATH, so that variable cannot
win):
| libsoup in the network process | rounds | crashes |
|---|---|---|
| bundled 3.6.5 | ~330 | 5 |
| system 3.6.6 | 840 | 0 |
With the preload, /proc/<pid>/maps of the network process shows the system
library mapped and the bundled one absent.
Rebuild the WebKit Linux bundles against libsoup ≥ 3.6.6. Until then,
projects hitting this need a per-run LD_PRELOAD of a fixed system libsoup,
which is what we shipped.
@playwright/test with retries: 0Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.