[1] CSS @function dashed-substitution use-after-free
Severity: High | Component: WebCore Style — Style::SubstitutionResolver | 4b97014
CSSVariableData::create가 이미 해제된 CustomProperty의 string backing에서 token 데이터를 재참조하는 heap use-after-free를 수정합니다. 단 한 줄의 @function snippet만으로 web content에서 이 경로에 도달할 수 있습니다. 재참조 전에 heap 재사용이 발생하면, freed-read가 recycled buffer에 대한 controlled-content read primitive로 전환됩니다. 이러한 이유로 Severity를 High로 평가합니다.
substitute()가 새로운 CSSVariableData를 생성할 때까지, resolve된 CustomProperty를 m_intermediateCustomProperties에 유지하도록 수정되었습니다. attr() tokenizer 출력에 사용되는 기존의 m_intermediateTokenStrings 방식과 동일한 패턴입니다. regression test는 @function --f(--a) { result: aaaa var(--a); }를 정의하고, target에 --x: --f(bbbb)를 적용한 뒤 getComputedStyle(target).getPropertyValue('--x')를 호출하여, substitution 경로가 해제된 token consumer를 통과하도록 유도합니다.
Source/WebCore/style/StyleSubstitutionResolver.cpp
@@ substituteDashedFunction
if (guard.isCyclicContext())
return false;
+ // Tokens가 resolvedResult의 string backing을 참조합니다. CSSVariableData가 재참조할 때까지 살아있어야 합니다.
tokens.appendVector(resolvedResult->tokens());
+ m_intermediateCustomProperties.append(WTF::move(resolvedResult));
return true;
}
@@ substitute
auto substitutedTokens = substituteTokenRange(value.m_data->tokenRange(), context);
if (!substitutedTokens) {
m_intermediateTokenStrings.clear();
+ m_intermediateCustomProperties.clear();
return nullptr;
}
auto data = CSSVariableData::create(*substitutedTokens, m_isAttrTainted ? IsAttrTainted::Yes : IsAttrTainted::No, context);
m_intermediateTokenStrings.clear();
+ m_intermediateCustomProperties.clear();
return data;
Source/WebCore/style/StyleSubstitutionResolver.h
Vector<String> m_intermediateTokenStrings;
+ Vector<RefPtr<const CustomProperty>> m_intermediateCustomProperties;
LayoutTests/fast/css/variables/dashed-function-result-token-lifetime.html
+@function --f(--a) {
+ result: aaaa var(--a);
+}
+#target { --x: --f(bbbb); }
+var v = getComputedStyle(target).getPropertyValue('--x');
Patch Details
patch는 SubstitutionResolver에 새로운 멤버 Vector<RefPtr<const CustomProperty>> m_intermediateCustomProperties를 추가합니다. substituteDashedFunction에서는 tokens.appendVector(resolvedResult->tokens()) 직후, local RefPtr를 새로운 vector로 이동시킵니다. substitute에서는 기존의 m_intermediateTokenStrings.clear()와 쌍을 이루어, 실패 경로와 성공 경로 모두에서 새로운 vector를 비웁니다. 기존 로직의 구조 변경은 없으며, parallel lifetime anchor가 추가된 것이 변경의 전부입니다.
substitution 단계를 가로지르는 lifetime anchor가 누락되어, backing object보다 오래 살아남는 non-owning token view.
Background
CSS Mixins는 @function을 도입하여 CSS 수준의 함수 정의(@function --name(--arg) { result: <tokens>; })를 가능하게 합니다. 이 함수는 custom property 값 안에서 dashed-function reference로 호출됩니다. Style::SubstitutionResolver는 style computation 도중 이러한 참조를 재귀적으로 확장합니다. 중첩된 var() / attr() / dashed-function 호출을 단일 token vector로 펼친 뒤, CSSVariableData::create를 통해 CSSVariableData를 생성합니다.
CSSVariableData는 파싱되지 않은 CSS 값의 storage 객체입니다. CSSParserToken은 외부 소유의 backing string에 대한 non-owning view를 저장하는 value-type token으로, backing string은 보통 upstream CustomProperty나 parser-allocated buffer가 소유하는 StringImpl payload입니다. Style::CustomProperty는 파싱/토크나이즈된 custom-property 값을 담는 reference-counted holder입니다. CustomProperty::tokens()를 호출하면 token이 반환되지만, 해당 token은 CustomProperty 자체가 소유하는 string storage를 참조합니다.
기존에 이미 m_intermediateTokenStrings 필드가 lifetime anchor로 존재했습니다. attr() tokenization 도중 생성되는 일시적인 string의 backing을 유지하여, 최종 CSSVariableData::create가 데이터를 재참조할 때까지 살아있게 하는 역할을 합니다.
Analysis
패치 이전에는, substituteDashedFunction이 tokens.appendVector(resolvedResult->tokens())를 호출한 뒤 반환될 때 local RefPtr<const CustomProperty> resolvedResult가 scope 종료 시점에 drop되었습니다. resolvedResult가 소멸되면, 해당 객체가 유지하던 StringImpl이 해제될 수 있었습니다. 이때 resolver의 tokens buffer에 이미 추가된 token view들은 여전히 그 storage를 가리키는 상태였습니다. 이후 substitute()의 CSSVariableData::create(*substitutedTokens, ...)가 dangling token을 참조하게 되고, 이 시점에는 이미 original backing이 소멸된 상태이므로 ASAN이 heap-use-after-free를 보고합니다.
이것은 "tokens가 외부 소유의 string을 view한다"는 전형적인 lifetime 문제입니다. SubstitutionResolver에는 attr() 경로에서 이미 동일한 문제의 해법이 적용되어 있었습니다. CSS Mixins / dashed-function 지원이 추가될 때 CustomProperty 소유의 token backing에 대해 동일한 문제가 재발했지만, 대응하는 lifetime anchor가 함께 추가되지 않았습니다. 기존 attr() lifetime fix에서 variant analysis를 수행하면 누락된 anchor를 기계적으로 도출할 수 있습니다.
Aa Aaaaaaaa Aaa Aaaaa Aaaaaaaaaa Aaaaaaaa a Aaaaaaa Aaaa Aaaaaaaaa Aaa Aaaaa Aaaaaaaa Aaaaa Aaaaaaaaaaa Aaa a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aa Aa Aa Aaa Aa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaa Aa Aaaaaa Aaaaa Aaaaaaaa Aaaaaaa Aaa Aa Aaaaa Aa Aaa Aaaaaaaaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa a Aa Aaaaaa Aaaaa Aaaaa Aaaa Aaaa
a Aaaa Aaaa Aaaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaa Aaaaaaaaaaaa Aaaaa Aaaaa Aaaa Aaa Aaaaaaaaaaaa Aaaaa Aaa Aa Aaa Aaaaaa Aaaaaaaaaaa Aaaaa a a Aaa Aaaaaaaaaaa a Aaaaaaaa Aa a a Aaa Aaaaaaa Aaaa Aaaa Aaaa Aaa Aaaa Aaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaa Aa Aaaa Aaa Aaaa Aa Aaaa Aaaaa Aaa Aa Aa Aa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaa Aa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaaaaaaa Aaa Aaaa Aaa Aaaaaaaa Aaa Aaaaaaaa Aaaaaaa Aa Aaaaaaaaaaaaaa Aaaaa Aaaaaaaaaa Aaa Aaaa Aaaaa
a Aaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaaa Aaa Aaaa Aaa Aa Aaaaaaaaaaaaaa Aaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaa Aa Aaa Aa Aaaa Aaaaa Aaa Aaaaaaaa Aa Aaa Aaaaaaaa Aaaaaaaaaaa Aaa Aa Aaa Aaaa Aaaaaaaaa Aaaa Aaaa Aaa a Aa Aaaaaa Aa Aaaaaaaa Aaaaa Aaaaaaaaaaa Aaa Aaa Aaaaaaaaaaaa Aaaaa Aaa a Aaaaa
🔒The lifetime model behind CSS variable token resolution and what happens when a substitution stage drops its backing object too early.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaaaaa Aaaaaaaaaa Aaaaa Aaaaaaaaaaa Aaaaaaaaaaaa Aaa Aa Aaaaaaa Aaaaaaa Aa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaaa Aaaaaaaa Aa Aaa Aaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aaa Aaaaaaaaaaaa Aaaaaa Aaaa Aaaaaa a Aaaa Aaaaaa Aaaaaaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aa Aaaaaaa Aaaa Aaaa
a Aaaaaaaaaaaaaaaaaa Aaaaaaaa Aaa a a Aaaaaaa Aaaa Aaaaaa Aaaaaaaa Aa Aaaa Aaaaa a Aaaaaaaaaaaaaaaa a Aaaaaaaaaaaaaaaaaa Aaaaaa a Aa Aaaa Aaaaaaa Aaaa Aa Aaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aa Aaaa Aaaa Aaaaaaaaaaaaaaaa Aaa Aaaaaa a Aa Aaaa Aaaaaa Aaa Aaaaa Aa Aa Aaa Aa Aaaaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaa a Aaaa Aaaaaaaaa Aaaa Aa Aa Aa Aaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaa Aaaaaa Aa Aaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaa Aaa Aaaaaa Aaa Aa Aaaaaaaa Aaaaaa Aaaaa Aaa Aaa Aaaaaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aaaa Aaaaa
a Aaaa Aa a Aaaaaaaa Aaaaaaa Aaaaaa Aa Aaaa Aaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaa Aa Aaa Aaa Aaa Aaa Aaaaaaaa Aaaaaaa Aa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaa a Aaaaaaaaaaa Aaa Aa Aa Aaaaaaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aa Aaa Aa a Aaaaaaaa Aaaaaaa Aa Aa Aaa Aaaaaa
a Aaaaaaaaa Aa Aa Aaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaa Aaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaa Aaaa Aaa Aaaa Aaaaa Aaaaaa Aa Aaaaa Aaa Aaaa Aaaaaa Aa Aaaa Aaa Aaaaa Aa Aaa Aaaaa Aaaa Aaaaaaaa Aaaaaaaa Aaaaaa Aaa Aaa Aaa Aaaaaa Aa Aaa Aaaaaaa
🔒Several reusable lifetime-anchor audit patterns identified across the CSS substitution pipeline, with concrete starting points for variant discovery.
더 확인하려면 구독해 주세요