From c217c57b5041cd317fc946dc447bf2cffc6bdbb3 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 24 Aug 2026 17:04:33 +0000 Subject: [PATCH] Bytecode cache: return null from Encoder::release when the payload allocation fails Encoder::release allocated the final contiguous payload with MallocSpan::malloc, which is crash-on-failure by contract. On Windows release builds that contract does not hold: bmalloc's BCRASH() is compiled as a call through a null function pointer, clang treats that as unreachable and deletes the null check in bmalloc::api::malloc, so fastMalloc returns null on OOM. The encoder then memcpy'd every page into a null buffer (bun 1.4.0 Windows x64, Sentry BUN-4RGJ, fault address 0x20 inside memcpy). The payload is the largest single allocation the encoder makes, and a bytecode cache is optional. Allocate it with tryMalloc and, if that fails, set BytecodeCacheError::StandardError(ENOMEM) and return null. Every encodeCodeBlock, encodeFunctionCodeBlock and encodeBuiltinFunction caller already handles a null result (release already returns null for a missing page and for a failed mapped write), so an embedder skips the cache for that module instead of crashing. The BCRASH() miscompile itself is fixed separately (#316). --- Source/JavaScriptCore/runtime/CachedTypes.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/JavaScriptCore/runtime/CachedTypes.cpp b/Source/JavaScriptCore/runtime/CachedTypes.cpp index aa6ab305398f..671df2e2fe21 100644 --- a/Source/JavaScriptCore/runtime/CachedTypes.cpp +++ b/Source/JavaScriptCore/runtime/CachedTypes.cpp @@ -721,7 +721,12 @@ class Encoder { } size_t size = m_baseOffset + m_currentPage->size(); - auto buffer = MallocSpan::malloc(size); + // The payload is the encoder's largest allocation and the cache is optional: report ENOMEM instead of crashing. + auto buffer = MallocSpan::tryMalloc(size); + if (!buffer) { + error = BytecodeCacheError::StandardError(ENOMEM); + return nullptr; + } auto bufferSpan = buffer.mutableSpan(); for (const auto& page : m_pages) memcpySpan(consumeSpan(bufferSpan, page.size()), page.span());