Bug 314526

Summary: REGRESSION (312740@main): [ASan/TSan] `angle::PoolAllocator` has inconsistent layout across translation units, crashing GPU process on WebGL shader compile
Product: WebKit Reporter: David Kilzer (:ddkilzer) <ddkilzer>
Component: WebGLAssignee: David Kilzer (:ddkilzer) <ddkilzer>
Status: RESOLVED FIXED    
Severity: Normal CC: djg, kbr, kkinnunen, webkit-bug-importer
Priority: P2 Keywords: InRadar, Regression
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 313441    
Bug Blocks:    

David Kilzer (:ddkilzer)
Reported 2026-05-10 21:28:27 PDT
ASan (and TSan) builds of WebKit cause any WebGL shader compile performed in the GPU process to abort with an AddressSanitizer stack-buffer-overflow report in `angle::PoolAllocator::PoolAllocator()`. This is observed across the majority of tests in `LayoutTests/fast/canvas/webgl/` on ASan builds. Stack trace (representative crash thread, from a local ASan build): ``` ==PID==ERROR: AddressSanitizer: stack-buffer-overflow WRITE of size 88 at 0x... thread TN 0 libclang_rt.asan_osx_dynamic.dylib: __asan_memset 1 libANGLE-shared.dylib: angle::PoolAllocator::PoolAllocator() 2 libANGLE-shared.dylib: sh::TCompiler::compile(angle::Span<char const* const>, ShCompileOptions const&) 3 libANGLE-shared.dylib: rx::ShaderTranslateTask::translate(void*, ShCompileOptions const&, std::string const&) 4 libANGLE-shared.dylib: gl::CompileTask::operator()() 5 libANGLE-shared.dylib: angle::UnlockedTailCall::runImpl(void*) 6 WebCore: WebCore::GraphicsContextGLANGLE::compileShader(unsigned int) 7 WebKit: WebKit::RemoteGraphicsContextGL::compileShader(unsigned int) 8 WebKit: WebKit::RemoteGraphicsContextGL::didReceiveStreamMessage(IPC::StreamServerConnection&, IPC::Decoder&) [...] ``` The ASan frame report shows the compiler-allocated stack slot for `scopedAlloc` (a `TScopedPoolAllocator` whose only member is an `angle::PoolAllocator`) is 24 bytes wide, but `angle::PoolAllocator::PoolAllocator()` performs an 88-byte `__asan_memset` to zero-initialize its members. The call writes past the end of the 24-byte slot, corrupting the adjacent ASan redzone and other stack locals. The 88-byte write size comes from the layout `std::vector<Segment> mSingleObjectSegments` + `Span<uint8_t> mCurrentPool` + `std::vector<Segment> mPoolSegments` + `std::vector<Segment> mUnusedSegments`. The 24-byte layout contains only `mSingleObjectSegments`. The three extra vectors are conditionally present under `#if !defined(ANGLE_DISABLE_POOL_ALLOC)` in `Source/ThirdParty/ANGLE/src/common/PoolAlloc.h`. `ANGLE_DISABLE_POOL_ALLOC` is defined at the top of `PoolAlloc.h` based on whether `ANGLE_WITH_ASAN` or `ANGLE_WITH_TSAN` is visible there. Those macros are defined in `common/platform.h` from `__has_feature()`. The `#if defined(ANGLE_WITH_ASAN) || defined(ANGLE_WITH_TSAN)` check in `PoolAlloc.h` runs BEFORE `common/platform.h` is transitively included (via `common/angleutils.h`, further down in the same header). As a result, whether the sanitizer macros are visible at that check depends on which headers the including translation unit already pulled in: * `Source/ThirdParty/ANGLE/src/common/PoolAlloc.cpp` includes `common/PoolAlloc.h` as its first include. At the macro check in `PoolAlloc.h`, `platform.h` has not been processed yet, so `ANGLE_WITH_ASAN` is NOT visible -> `ANGLE_DISABLE_POOL_ALLOC` is NOT defined -> `PoolAllocator` has the 88-byte layout, and the emitted `PoolAllocator::PoolAllocator()` body (defaulted) zero-initializes 88 bytes. * `Source/ThirdParty/ANGLE/src/compiler/translator/Compiler.cpp` (the `sh::TCompiler::compile` caller) includes `compiler/translator/Compiler.h` first, which transitively pulls in `common/platform.h` via `Diagnostics.h`/`SymbolTable.h` BEFORE `PoolAlloc.h` is processed. At the macro check, `ANGLE_WITH_ASAN` IS visible -> `ANGLE_DISABLE_POOL_ALLOC` IS defined -> `PoolAllocator` has the 24-byte layout, and the stack slot for `scopedAlloc.mAllocator` is 24 bytes wide. This is an ODR violation caused by a preprocessor macro check running before the macro-defining header is included. Introduced by Bug 313441 / 312740@main, where the `ANGLE_DISABLE_POOL_ALLOC` selection was added at the top of `PoolAlloc.h`. The previous macro selection at the same position (`#if !defined(NDEBUG)`) did not have this failure mode because `NDEBUG` is a standard compiler-provided macro that is visible regardless of include order. Steps to reproduce: 1. Build WebKit for macOS with ASan. 2. Run any WebGL layout test that compiles a shader: `Tools/Scripts/run-webkit-tests --no-build --release --no-timeout --no-show-results fast/canvas/webgl/gl-getshadersource.html` (or any similar test). 3. Observe GPU-process crash with ASan `stack-buffer-overflow` report in `WebKitBuild/Release/layout-test-results/com.apple.WebKit.GPU.Development-*-crash-log.txt`. Binary-level confirmation from `otool -tv` of `libANGLE-shared.dylib` pre-patch shows `angle::PoolAllocator::PoolAllocator()` calling `__asan_memset` with `w2 = #0x58` (88 bytes); consumer translation units allocate only 24 bytes of stack for the corresponding `scopedAlloc`.
Attachments
David Kilzer (:ddkilzer)
Comment 1 2026-05-10 21:28:29 PDT
David Kilzer (:ddkilzer)
Comment 2 2026-05-10 21:44:11 PDT
EWS
Comment 3 2026-05-10 22:33:58 PDT
Committed 312987@main (346deb2b1c82): <https://commits.webkit.org/312987@main> Reviewed commits have been landed. Closing PR #64653 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.