[14] SWServer dereferences unchecked HashMap end iterator
Severity: Low | Component: WebCore Service Worker Server | 3bce213
두 client map이 race condition으로 동기화가 어긋날 때만 도달 가능한 HashMap end-iterator 역참조를 수정합니다. commit message에는 재현 방법이 알려져 있지 않다고 명시됩니다. 영향은 service-worker bookkeeping 경로에서의 crash에 국한되며, 공격자가 메모리 읽기/쓰기를 제어할 수 있다는 근거는 없습니다.
m_clientIdentifiersPerOrigin과 m_clientsById가 동기화 상태를 잃으면, topLevelServiceWorkerClientFromPageIdentifier()에서 crash가 발생합니다. 이 함수는 m_clientIdentifiersPerOrigin에서 client identifier를 순회하며 각각에 대해 m_clientsById.find()를 호출합니다. 그러나 역참조 전에 반환값이 m_clientsById.end()와 같은지 전혀 확인하지 않습니다.
Source/WebCore/workers/service/server/SWServer.cpp
auto clientIterator = m_clientsById.find(clientIdentifier);
-ASSERT(clientIterator != m_clientsById.end());
+if (clientIterator == m_clientsById.end()) [[unlikely]] {
+ ASSERT_NOT_REACHED();
+ return std::nullopt;
+}
return clientIterator->value;
...
for (auto clientIdentifier : iterator->value.identifiers) {
auto clientIterator = m_clientsById.find(clientIdentifier);
+ if (clientIterator == m_clientsById.end()) {
+ ASSERT_NOT_REACHED();
+ continue;
+ }
if (clientIterator->value->frameType == ServiceWorkerClientFrameType::TopLevel && clientIterator->value->pageIdentifier == pageIdentifier)
return clientIterator->value;
}
Patch Details
serviceWorkerClientWithOriginByID()에서는 기존의 ASSERT(clientIterator != m_clientsById.end())가 실제 end-iterator 검사로 변경되었습니다. end 조건에 해당하면 release 빌드에서 std::nullopt를 반환합니다. topLevelServiceWorkerClientFromPageIdentifier()에서는 iterator->value.identifiers를 순회하며 m_clientsById.find(clientIdentifier)를 호출하던 루프가, 기존에는 결과를 무조건 역참조했습니다. 이제 end-iterator 검사와 함께 ASSERT_NOT_REACHED() 및 continue가 추가되어, 해당 identifier가 없는 항목은 건너뜁니다.
비동기 lifecycle 경로에서 paired-map 불변 조건이 어긋날 수 있는 환경에서, end-check 없이 HashMap iterator를 역참조하는 패턴.
Background
SWServer는 세션의 service-worker 상태를 process 경계를 넘어 관리하는 소유자입니다. m_clientIdentifiersPerOrigin은 HashMap<ClientOrigin, ...> 타입으로, 각 origin에 client로 등록된 모든 ScriptExecutionContextIdentifier를 identifiers 집합으로 관리합니다. m_clientsById는 동일한 identifier를 키로 사용하는 별도의 HashMap<ScriptExecutionContextIdentifier, RefPtr<ServiceWorkerClientData>>입니다. 두 map의 불변 조건은, per-origin identifiers 집합에 등장하는 모든 identifier가 반드시 m_clientsById에도 존재해야 한다는 전제에 기반합니다. HashMap::find()는 키가 없을 때 end()와 같은 iterator를 반환하는데, 이 end iterator를 역참조하면 undefined behaviour가 됩니다. WebCore의 ASSERT(...)는 release 빌드에서 컴파일 시 제거되므로, ASSERT만으로는 런타임 보호 효과를 기대할 수 없습니다.
Analysis
topLevelServiceWorkerClientFromPageIdentifier()는 m_clientIdentifiersPerOrigin의 per-origin 집합에서 client identifier를 가져온 뒤, m_clientsById.find(clientIdentifier)의 반환값이 m_clientsById.end()와 같은지 확인하지 않고 clientIterator->value를 역참조했습니다. 두 map은 서로 다른 code path를 통해 채워지고 해제됩니다. commit message에서는 이를, 두 map의 동기화가 어긋날 수 있는 race로 설명합니다.
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaa Aaa Aaaaa Aaaaa Aaa Aaaaa Aaaaaaa Aaaaa Aaaaaaa Aaaa Aaa Aaaaaaaaa Aaa Aaaaa Aaaaaa
a Aaaa Aaaaaaaaaaa Aaaaa Aaaaaaa Aaaaaaaa Aaaa Aaaaaaaa a Aaaaaa Aaaa Aaaa Aaaa Aaaaaaa Aaaaaaaaaaaa Aaaa Aaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaa Aaaaaaaaa Aaaaaa Aaaa Aaa Aaa Aaa a a Aaa Aaa Aaaaa Aaa Aaaaaaaaaaa Aa Aaaa a Aa Aa Aaaaaaaa Aaa Aaaa Aaaaa Aaaaa Aaaaaaaaaaa Aa Aaa Aaaa Aaaa Aaaaaaaa Aaaaa Aaaa Aaaaaa Aaaaaa Aaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaa Aaaaaaaaaa Aaa Aaaa Aaaaaa Aaa Aaaa Aa Aaaaa a Aaaa Aaaaa Aaaaa
🔒The lifetime and synchronization implications between two parallel service-worker bookkeeping maps are explored, along with the realistic ceiling on what this crash can be escalated to.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaaaaaaa Aa Aaa Aaaa Aaaaaa Aaaaaaa Aa a Aaaa Aa Aa Aa Aaa Aaa Aaaaaa Aaa Aaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa a Aa Aaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaa Aa a Aaaaaaaaa Aa Aaaaaaaaa Aaaaa Aaa Aaaa Aaaa
a Aaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaa Aa Aa Aaa Aa Aaaaaaaaa Aaaaa Aaa Aaaa Aaaa a Aaa Aaa Aaa Aaaa Aaa Aaaaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaa Aaaaaaa Aaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aaaa Aaa Aaaa Aa Aa Aaa Aa Aa Aaa Aaa Aaaa Aaaa Aa Aaa Aa Aaaa Aa Aaaa Aa Aa a Aa Aa Aaaaaa Aaa a Aaaa Aaaa Aaaa
a Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aaaaaaaaa Aaaaaa Aa Aaaaa Aaaaaa Aaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aa Aaaa Aaaaa Aaaa Aa Aaaa Aaa Aaa Aaaa Aaaa
🔒Multiple reusable audit patterns identified across the service-worker subsystem, with concrete grep targets for finding sibling instances of the same defensive gap.
더 확인하려면 구독해 주세요