[3] ZStream: deflateEnd() called after inflateInit2() via DecompressionStream
Severity: Medium | Component: WebCore Compression Streams | 43a1b0b
Commit 작성자가 로컬에서 crash를 재현하지 못했다는 점을 감안해 Medium으로 평가되었습니다. 관찰 가능한 영향은 inflate 초기화된 zlib 상태에서 deflateEnd()를 호출함으로써 발생하는 invalid free이며, heap metadata를 손상시킬 수 있는 undefined behavior를 유발합니다. 다만 실제 exploit 가능성은 zlib 내부 struct layout과 heap 상태에 달려 있으며, diff만으로는 이를 확인할 수 없습니다.
ZStream destructor는 스트림이 압축용으로 초기화되었는지, 압축 해제용으로 초기화되었는지에 관계없이 deflateEnd()를 무조건 호출했습니다. 이번 패치에서는 초기화 모드를 추적하기 위한 m_operation 멤버가 추가되었고, 이를 기반으로 분기 처리가 이루어집니다.
Source/WebCore/Modules/compression/ZStream.cpp
+ m_operation = operation;
m_isInitialized = true;
return true;
}
ZStream::~ZStream()
{
- if (m_isInitialized)
+ if (!m_isInitialized)
+ return;
+
+ if (m_operation == Operation::Compression)
deflateEnd(&m_stream);
+ else
+ inflateEnd(&m_stream);
}
Source/WebCore/Modules/compression/ZStream.h
z_stream m_stream;
+ Operation m_operation { Operation::Compression };
bool m_isInitialized { false };
Patch Details
ZStream 클래스에 Operation 타입의 멤버 변수 m_operation이 추가되었습니다. 이 enum은 Compression과 Decompression을 구분하며, initializeIfNecessary() 호출 시점에 값이 저장됩니다. Destructor는 기존의 단순한 if (m_isInitialized) deflateEnd(...) 구조에서 벗어나, early-return guard를 먼저 수행한 뒤 operation에 따라 분기하는 구조로 재작성되었습니다. 압축 모드에서는 deflateEnd, 압축 해제 모드에서는 inflateEnd를 호출합니다. m_operation의 기본값은 Operation::Compression이며, m_isInitialized는 별도로 관리됩니다.
리소스 정리 불일치 — destructor가 실제로 초기화된 리소스 타입과 다른 해제 함수를 호출하는 패턴.
Background
Compression Streams API(CompressionStream / DecompressionStream)는 웹 표준 JavaScript API로, 스트리밍 방식의 압축 및 압축 해제를 제공합니다. 모든 웹 페이지에서 접근 가능합니다. WebKit 구현체는 Source/WebCore/Modules/compression/의 ZStream 클래스를 통해 zlib을 감싸고 있습니다. zlib은 초기화와 종료를 쌍으로 제공합니다. 압축에는 deflateInit2()/deflateEnd(), 압축 해제에는 inflateInit2()/inflateEnd()를 사용합니다. 두 함수 쌍은 동일한 z_stream 객체 내부에서 서로 완전히 다른 내부 상태를 관리합니다. 할당하는 내부 버퍼도 다르고, 사용하는 함수 포인터도 다르며, bookkeeping 구조도 다릅니다. 초기화에 사용된 함수와 다른 종료 함수를 호출하는 것은 zlib API 계약상 undefined behavior입니다.
Analysis
근본 원인은 전형적인 리소스 정리 불일치 버그입니다. ZStream 클래스는 zlib을 얇게 감싸는 wrapper로 설계되었지만, 어느 초기화 경로를 거쳤는지를 추적하지 않았습니다. 그 결과 항상 압축 정리 경로가 실행되었습니다. JavaScript에서 DecompressionStream을 생성하면(예: new DecompressionStream('gzip')), 내부 z_stream이 inflateInit2()를 통해 초기화됩니다. 이후 GC에 의해 수집되거나 scope를 벗어나 소멸될 때 destructor가 deflateEnd(&m_stream)을 호출합니다. 이 시점에서 inflate 내부 상태를 deflate 내부 상태로 오해석하게 됩니다. 결과적으로 zlib은 내부 allocation을 잘못 해석하고, 잘못된 내부 구조에서 파생된 유효하지 않은 포인터에 free()를 호출하게 됩니다.
Aaaaaaa Aaa Aaaa Aaa Aaaaa Aa Aaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaa Aa Aaaaaaa Aaaa Aaa a Aaaaaaa Aaaa Aaaaaaaaaaaaaaa Aaaaa Aaaaa Aa Aaaaaaaaaaa Aa Aaa Aaaa Aa Aaaa Aaaaaa Aaaa Aaa Aaaa Aa Aaaaa Aaaaaaaaa Aaaaa Aa Aaa Aa Aaaa Aaaa Aaa Aaa Aaaa Aaa a Aaaaa a Aa Aaaaaaa Aaaaa Aaa Aaaaaaaaaa Aaaaaaaaaa Aaa Aaaa Aaaaa
Aa Aaaaaaa Aaaa Aaaa Aaa Aa Aaaaaa Aaaaaaa Aaaa Aaaaaaaaa Aaa Aaaa Aa Aaaaa Aaaaaa Aaaa Aaaaa Aaaaaa Aaaa Aaaaa Aaaaaa Aa Aa Aaaa Aaaaaaaaaa Aaaaa Aaaaa Aa Aaaa Aaaaaaaaa Aaa Aaaa a Aaa Aaaaaa Aaaa Aaaaa Aaaa Aaaaa Aaaa Aaaaaaa Aaa a Aa Aa Aaaaa Aaa a Aaa a Aaaaa
a Aaaaaaaaaaaaaa Aaaaaaaa Aaaaaaaa Aaaaaa Aaaaaaa Aaaaaaa Aaaaaaaaaaa Aaaaaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaaaa Aa Aaaaaa Aaaaaaa Aaa Aaaa Aaaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaaaaaaaa Aa Aaaa Aaaaa Aaa Aaaa Aaaaa Aaaa Aa a Aa Aaaa Aaa a Aa Aaaa a Aaaa Aaaaaaaaaaa Aaa Aaaa Aaaaa Aa Aaa Aaa Aaaa Aaa Aaaaaaa Aaaaaaa Aaaaaa
a Aa Aaa Aaa Aaa Aaaaa Aaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaa Aaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaaa Aaaaa Aaa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaaa Aaaa Aaaaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaa Aaaa Aa Aaa Aaa Aaa Aa Aaaaaa
Aaaaaaaaa Aaaaaaaa Aaaaaaaa Aa Aa Aaa Aaaaa Aa Aaaaaaa Aaaaa Aaaaa Aaa Aaaaa Aa Aaa Aaaa a Aaa Aaaaaa Aa Aaa Aaaaaa Aaaaaaaaaa Aaa Aaaaaa Aaaa Aa Aaaa Aaa Aa Aaaaa Aaa Aa Aaaaa Aa Aaaaa Aaa Aa Aa Aaaaaa
🔒The heap corruption implications of this mismatched cleanup are explored, including exploitation feasibility from web content
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaaa Aaa Aaa Aaa Aaaaa Aa Aa Aaa Aaaa Aaaaaaaa Aaaaaaaaaa Aaa Aaaa a Aaaaa Aaaa Aaaa Aa Aaaaaaa Aaaaaaa Aaaa Aaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaaaaaaaaa Aaa Aaaaaaaa Aaa Aa Aaaaaa Aaaaaaaaaaa Aa Aaa Aaa Aa Aaa Aa Aaa Aaaaa Aaaa Aaaa
a Aaaaaaaaaa Aaaa Aa Aaa Aaaa Aa Aa Aaaaaa Aaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaa Aaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaa Aaaaa Aaaaaa Aaaaaaaaaaa Aaaaaaaaaaaaa Aaaa Aaaa Aaaa Aaaaaaaaaaaa Aaaaa Aa Aaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaa Aaaa Aa Aaaa Aaaaaaaaaaaaaaaaaa Aaaaa a a Aa Aa Aaa Aaa Aaaa Aaaa Aaaaaaaaaaa Aa Aaaa Aaaaaaaaaaaaaaaa a Aaaaaa Aaaa Aa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaaa Aa Aaaa Aa Aaa Aaa Aaa Aaaa Aaaaa Aaaa Aaaa Aa Aaaa Aa Aaaa Aa Aaaaaaaaaa Aaaaaaa Aaaa Aaa Aaaa Aaa Aaaaaa
🔒Multiple audit patterns identified for mismatched resource cleanup across WebKit's native library wrappers
더 확인하려면 구독해 주세요