CVE-2024-27838
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.htmlLayoutTests/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>