Chrome · DNS
CVE-2026-17924
UAF in DNS
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifnet/dns/host_resolver_manager.cc |
modified | |
TEST_Fnet/dns/host_resolver_manager_unittest.cc |
modified |
Files Changed
net/dns/host_resolver_manager.ccnet/dns/host_resolver_manager_unittest.cc
Patch
From b23c7e608fe71898fc48ae85e019f7eadd890ad1 Mon Sep 17 00:00:00 2001
From: Kenichi Ishibashi <bashi@chromium.org>
Date: Mon, 01 Jun 2026 18:35:17 -0700
Subject: [PATCH] net: Fix potential teardown crash in HostResolverManager
During ResolveContext deregistration, HostResolverManager::RemoveAllJobs
cancels and removes all active jobs associated with the context.
If the destruction of a job (such as an outer NAT64 job) synchronously
triggers the cancellation and removal of a nested job (such as the inner
AAAA job for ipv4only.arpa), the iteration state in RemoveAllJobs could
be invalidated because the nested job erases itself from the jobs_ map
while it is being iterated.
This CL fixes the issue by collecting the Job objects to be removed in a
temporary vector during the iteration loop, and then explicitly
destroying them after the iteration is complete. This ensures that any
synchronous side-effects of job destruction do not interfere with the
map iteration.
Bug: 513714124
Change-Id: I7236ad5c3ac03c0fed0c6c99f81cdbae281f9165
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7881379
Reviewed-by: Adam Rice <ricea@chromium.org>
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1639847}
---
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index 20df619b..e583868 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -1638,14 +1638,20 @@
}
void HostResolverManager::RemoveAllJobs(const ResolveContext* context) {
+ // Job destructor can re-enter jobs_.erase() (e.g., via HostResolverNat64Task
+ // destructor destroying a nested RequestImpl whose CancelRequest
+ // synchronously removes a different Job). Collect the Jobs first and destroy
+ // them after the iteration loop to prevent iterator invalidation.
+ std::vector<std::unique_ptr<Job>> jobs_to_destroy;
for (auto it = jobs_.begin(); it != jobs_.end();) {
const JobKey& key = it->first;
if (&*key.resolve_context == context) {
- RemoveJob(it++);
+ jobs_to_destroy.push_back(RemoveJob(it++));
} else {
++it;
}
}
+ jobs_to_destroy.clear();
}
void HostResolverManager::AbortJobsWithoutTargetNetwork(bool in_progress_only) {
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 7d38cb9..300ade3 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -14574,6 +14574,49 @@
IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(false);
}
+// Regression test for crbug.com/513714124.
+//
+// When a ResolveContext is deregistered, all active jobs associated with it
+// are cancelled. For some requests (like NAT64 translation), a job may own
+// a nested request that is attached to a different job. If cancelling the
+// outer job synchronously cancels and removes the nested job, we must ensure
+// this nested removal does not invalidate the iteration state used to clean
+// up the remaining jobs.
+TEST_F(HostResolverManagerTest,
+ Nat64DeregisterContextDoesNotInvalidateIterator) {
+ HostResolver::ManagerOptions options = DefaultOptions();
+ CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
+ /*ipv6_reachable=*/true,
+ /*is_async=*/false,
+ /*ipv4_reachable=*/false);
+ proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6,
+ "64:ff9b::c000:aa,64:ff9b::c000:ab");
+
+ HostResolver::ResolveHostParameters params;
+ params.dns_query_type = DnsQueryType::A;
+ ResolveHostResponseHelper response(resolver_->CreateRequest(
+ HostPortPair("192.168.1.42", 80), NetworkAnonymizationKey(),
+ NetLogWithSource(), params, resolve_context_.get()));
+
+ ASSERT_FALSE(response.complete());
+ // Wait for the inner ipv4only.arpa system task's worker to block so both
+ // the outer NAT64 Job ({A}) and the inner Job ({AAAA}) are live in jobs_.
+ ASSERT_TRUE(proc_->WaitFor(1u));
+ ASSERT_EQ(2u, resolver_->num_jobs_for_testing());
+
+ // ResolveContext teardown while the inner ipv4only.arpa lookup is in
+ // flight. Without the fix this is a heap-use-after-free under ASan.
+ resolver_->DeregisterResolveContext(resolve_context_.get());
+
+ EXPECT_EQ(0u, resolver_->num_jobs_for_testing());
+
+ // Cleanup: release the blocked worker and re-register the context so
+ // TearDown's DeregisterResolveContext is balanced.
+ proc_->SignalAll();
+ base::ThreadPoolInstance::Get()->FlushForTesting();
+ resolver_->RegisterResolveContext(resolve_context_.get());
+}
+
TEST_F(HostResolverManagerDnsTest, ResolutionDetails_InsecureDnsSuccess) {
ChangeDnsConfig(CreateValidDnsConfig());
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/net/dns/host_resolver_manager_unittest.cc b/net/dns/host_resolver_manager_unittest.cc
index 7d38cb9..300ade3 100644
--- a/net/dns/host_resolver_manager_unittest.cc
+++ b/net/dns/host_resolver_manager_unittest.cc
@@ -14574,6 +14574,49 @@
IPv4AddressLiteralInIPv6OnlyNetworkBadAddressTest(false);
}
+// Regression test for crbug.com/513714124.
+//
+// When a ResolveContext is deregistered, all active jobs associated with it
+// are cancelled. For some requests (like NAT64 translation), a job may own
+// a nested request that is attached to a different job. If cancelling the
+// outer job synchronously cancels and removes the nested job, we must ensure
+// this nested removal does not invalidate the iteration state used to clean
+// up the remaining jobs.
+TEST_F(HostResolverManagerTest,
+ Nat64DeregisterContextDoesNotInvalidateIterator) {
+ HostResolver::ManagerOptions options = DefaultOptions();
+ CreateResolverWithOptionsAndParams(std::move(options), DefaultParams(proc_),
+ /*ipv6_reachable=*/true,
+ /*is_async=*/false,
+ /*ipv4_reachable=*/false);
+ proc_->AddRule("ipv4only.arpa", ADDRESS_FAMILY_IPV6,
+ "64:ff9b::c000:aa,64:ff9b::c000:ab");
+
+ HostResolver::ResolveHostParameters params;
+ params.dns_query_type = DnsQueryType::A;
+ ResolveHostResponseHelper response(resolver_->CreateRequest(
+ HostPortPair("192.168.1.42", 80), NetworkAnonymizationKey(),
+ NetLogWithSource(), params, resolve_context_.get()));
+
+ ASSERT_FALSE(response.complete());
+ // Wait for the inner ipv4only.arpa system task's worker to block so both
+ // the outer NAT64 Job ({A}) and the inner Job ({AAAA}) are live in jobs_.
+ ASSERT_TRUE(proc_->WaitFor(1u));
+ ASSERT_EQ(2u, resolver_->num_jobs_for_testing());
+
+ // ResolveContext teardown while the inner ipv4only.arpa lookup is in
+ // flight. Without the fix this is a heap-use-after-free under ASan.
+ resolver_->DeregisterResolveContext(resolve_context_.get());
+
+ EXPECT_EQ(0u, resolver_->num_jobs_for_testing());
+
+ // Cleanup: release the blocked worker and re-register the context so
+ // TearDown's DeregisterResolveContext is balanced.
+ proc_->SignalAll();
+ base::ThreadPoolInstance::Get()->FlushForTesting();
+ resolver_->RegisterResolveContext(resolve_context_.get());
+}
+
TEST_F(HostResolverManagerDnsTest, ResolutionDetails_InsecureDnsSuccess) {
ChangeDnsConfig(CreateValidDnsConfig());
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page