Released May 13, 2024

CVE-2024-27838

CVSS 6.5 severity medium CWE-79 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N

Overview

Impact:

Description: A maliciously crafted webpage may be able to fingerprint the user

Researchers: Emilio Cobos of Mozilla

Key insight. paintDecorations looked up the visited-dependent BACKGROUND color without honoring the paint behavior, so filters could read visited state through the background; passing paintBehavior makes the painter ignore visited styles where the result is observable (mirroring the earlier text-color fix).

Patch Details

diff --git a/LayoutTests/swipe/resources/swipe-test.js b/LayoutTests/swipe/resources/swipe-test.js
index a32ba832493c..132f6c9c6c7f 100644
--- a/LayoutTests/swipe/resources/swipe-test.js
+++ b/LayoutTests/swipe/resources/swipe-test.js
@@ -46,6 +46,8 @@ async function startSlowSwipeGesture()
     if (!window.eventSender)
         return;
 
+    log("startSlowSwipeGesture");
+
     await UIHelper.ensurePresentationUpdate();
 
     // Similar to uiController.beginBackSwipe(), but with a gap between events to allow
diff --git a/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt b/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
index 8215851db5ff..fd5b55d8f959 100644
--- a/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
+++ b/LayoutTests/swipe/swipe-back-with-active-wheel-listener-expected.txt
@@ -1,4 +1,5 @@
 Swipe target
+startSlowSwipeGesture
 didBeginSwipe
 completeSwipeGesture
 willEndSwipe
diff --git a/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt b/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
index 6bd807db86af..fd5b55d8f959 100644
--- a/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
+++ b/LayoutTests/swipe/swipe-back-with-passive-wheel-listener-expected.txt
@@ -1,5 +1,5 @@
 Swipe target
-startSwipeGesture
+startSlowSwipeGesture
 didBeginSwipe
 completeSwipeGesture
 willEndSwipe
diff --git a/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt b/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
index e7a0ac32652b..975b6f62289c 100644
--- a/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
+++ b/LayoutTests/swipe/wheel-prevent-default-prevents-swipe-back-expected.txt
@@ -1,3 +1,4 @@
 Swipe target
+startSlowSwipeGesture
 completeSwipeGesture
 

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2024-27838
Bugzilla 262337
Component WebCore
Bug Class LogicError
Severity medium
Commit f0fba73ace0f5a4e…
Advisory Apple Advisory

Terminology

  • :visited privacy protections — Browsers allow limited :visited styling but restrict readback so pages cannot detect which links a user has visited.
  • visitedDependentColor + paintBehavior — A style lookup that returns the visited or unvisited color; the paintBehavior tells it whether visited styles must be ignored for the current paint.
  • SVG/CSS filter side channel — A filter processes rendered pixels; if visited-dependent color enters the filter, the observable output can leak visited state.

Root Cause Analysis

This fixes a browsing-history disclosure (visited-link detection) via SVG/CSS filters applied to hyperlinks. WebKit lets :visited links be styled but tightly restricts what a page can observe about visited state to prevent history sniffing; painting code must ignore visited-dependent styles in contexts where the result could be read back. Applying an SVG filter to a hyperlink, however, could leak the visited state through the link’s BACKGROUND color: InlineBoxPainter::paintDecorations obtained the background color with style.visitedDependentColor(CSSPropertyBackgroundColor) WITHOUT passing the current paint behavior, so it used the visited-dependent background even when painting into a filter pipeline whose output the page can observe (per the averaging/filter attack described in arXiv:2305.12784). A page could thus set different a:visited background colors, apply an SVG filter, and infer from the filtered output whether a given link had been visited. The fix threads the paint behavior into the lookup: style.visitedDependentColor(CSSPropertyBackgroundColor, m_paintInfo.paintBehavior), so when the paint behavior indicates visited styles must be ignored (e.g. while producing filter input), the unvisited background color is used instead of the visited-dependent one. This is a follow-up to an earlier fix (266683@main) that handled the text color; this commit closes the same leak for background-color. The restored invariant is that visited-dependent background color is not used when painting in a context that could expose it, so filters cannot recover visited state. The regression test gives a:link and a:visited different background colors and checks the filtered rendering does not reveal visited state.

Attack Path

1. Style visited backgrounds

Define a:link and a:visited with different background-color values for target links.

2. Apply an SVG filter

Apply an SVG/CSS filter to the hyperlink so the visited-dependent background is drawn into the filter pipeline.

3. Observe the filtered output

Read back the filtered rendering (per the averaging attack) to distinguish the visited vs unvisited background.

4. Infer history

Deduce whether each link was visited, leaking the user’s browsing history.

Impact Assessment

A browsing-history disclosure (privacy) issue in the WebContent process with no memory corruption: SVG/CSS filters on hyperlinks could leak visited-link state via the background color. It reveals which sites a user has visited — a meaningful privacy leak — but does not by itself corrupt memory or execute code.

Audit Directions

visitedDependentColor call sites

Grep for style.visitedDependentColor(…) that omit a paintBehavior argument in painting code (backgrounds, borders, shadows, decorations) reachable inside filters/readback.

Visited-state readback surfaces

Review filter, canvas-drawImage, and paint-into-image paths for use of visited-dependent styles that could leak history.

Changed Functions

Function File Change Note
InlineBoxPainter::paintDecorations Source/WebCore/rendering/InlineBoxPainter.cpp modified Passes m_paintInfo.paintBehavior to style.visitedDependentColor(CSSPropertyBackgroundColor, …) so visited-dependent background is ignored when painting in a context (e.g. filter input) that could expose visited state.

Files Changed

Source Files

  • Source/WebCore/rendering/InlineBoxPainter.cpp

Test Files

  • LayoutTests/css3/filters/filter-visited-links-expected.html
  • LayoutTests/css3/filters/filter-visited-links.html

Regression Test

The fix ships this proof-of-concept / regression test:

diff --git a/LayoutTests/css3/filters/filter-visited-links-expected.html b/LayoutTests/css3/filters/filter-visited-links-expected.html
index d437c49f7f21..a9b554ce670b 100644
--- a/LayoutTests/css3/filters/filter-visited-links-expected.html
+++ b/LayoutTests/css3/filters/filter-visited-links-expected.html
@@ -27,9 +27,11 @@
     a {
         color: blue;
         text-decoration: none;
+        background-color: yellow;
     }
     a:visited {
         color: purple;
+        background-color: orange;
     }
 </style>
 <body>
diff --git a/LayoutTests/css3/filters/filter-visited-links.html b/LayoutTests/css3/filters/filter-visited-links.html
index 2c578423fdae..45b19c84ca95 100644
--- a/LayoutTests/css3/filters/filter-visited-links.html
+++ b/LayoutTests/css3/filters/filter-visited-links.html
@@ -27,9 +27,11 @@
     a {
         color: blue;
         text-decoration: none;
+        background-color: yellow;
     }
     a:visited {
         color: purple;
+        background-color: orange;
     }
 </style>
 <body>