ede5159e83ef3f354974f4b10ea5dc36a8de561e Avoid crash in BoxPainterBase
The bug at a glance
The diff adds a guard so BoxPainterBase does not paint a background image that is still a pending image; the commit calls it a crash with no stated memory-corruption impact, consistent with a renderer null-deref/CHECK-style crash (inferred). Reachable by ordinary web content but impact appears limited to a tab crash.
Web content can crash its own renderer by painting a top-layer whose paint() background image is still pending during an overlay/display transition-out. Defenders care mainly because paint code assuming resolved StyleImages could hide a worse dereference on other paths.
Root cause
In BoxPainterBase’s fill-layer painting, the image-painting branch ran whenever fill_layer_info.should_paint_image was true, even if fill_layer_info.image was still a pending StyleImage (not yet resolved, e.g. a paint() worklet image on a dialog transitioning out of the top layer). Using the pending image to prepare compositing/mask state or paint crashed (exact faulting call not shown in diff). The patch adds !fill_layer_info.image->IsPendingImage() to the condition so pending images are skipped.
Key code
Skip painting pending background images
- if (fill_layer_info.should_paint_image) {
+ if (fill_layer_info.should_paint_image &&
+ !fill_layer_info.image->IsPendingImage()) {
Patch walkthrough
third_party/blink/renderer/core/paint/box_painter_base.cc— Adds an IsPendingImage() check before the background-image painting branch, preventing use of an unresolved StyleImage during paint.third_party/blink/web_tests/external/wpt/css/css-transitions/top-layer-background-image-transition-crash.html— New WPT crash test: a with background-image: paint(…) and overlay/display allow-discrete transitions is shown modally then closed; author notes it only reproduces in a normal browser, not the test runners.
Background
Pending StyleImage — A placeholder image value in computed style that has not yet been resolved to a real image resource/generator.
overlay / display allow-discrete transitions — Let a top-layer element such as a closed keep rendering during its exit transition, creating unusual style states at paint time.
CSS paint() — CSS Paint API (Houdini) image function backed by a paint worklet.
Vulnerability window
- Reachability — Ordinary web content in a stock build: CSS on a plus showModal()/close(), no flags or user gesture needed per the test.
- Trigger — Closing the dialog while overlay/display transitions keep it painted leaves its paint() background as a pending image when BoxPainterBase runs (about one rAF later per the commit message).
- Primitive — Renderer crash in background painting; no memory-corruption primitive is evidenced in the diff.
Proof of concept
Conceptually: style a with a paint() background image and allow-discrete overlay/display transitions, open it modally, add the transition class, and close it; on the next frame the painter encounters a pending image. The added WPT crash test is the reference reproducer.
Exploitation
- Denial of service — Crashes the attacker’s own renderer/tab; no escalation path shown (inferred).
Detection & hunting
For defenders and detection engineers:
- grep -n ‘should_paint_image’ third_party/blink/renderer/core/paint/ — Confirm every image-paint branch checks IsPendingImage() or otherwise guarantees a resolved image.
- Crash reports with BoxPainterBase::PaintFillLayer frames — Triage renderer crashes involving top-layer transitions and paint() images.
Audit directions
- Other painters consuming StyleImage (border-image, mask, list-style-image, content images) — Look for paths that assume a non-pending image during top-layer exit transitions.
- Style recalc for elements in overlay transition (top layer removal) — Check why pending images survive to paint for these elements and whether other unresolved style values do too.