[dns-client] fix double-free of mSavedResponse on duplicate response (#13060)

Fix a double-free of `mSavedResponse` in `Dns::Client` when processing
duplicate DNS responses matching an active query.

When an SRV/TXT query needs to resolve a host address (AAAA), the DNS
client allocates a chained `newQuery` to handle it. If duplicate
responses are processed before the query chain is finalized, they
trigger multiple AAAA resolution allocations for the same parent query.
Because the new query inherits `mSavedResponse` from the parent query's
`QueryInfo`, multiple chained queries end up aliasing/sharing the same
cloned `mSavedResponse` message. During finalization, `FreeQuery`
walks the chain and frees `mSavedResponse` for each query, leading to
a double-free of the shared `Message` and free-list/heap corruption.

This commit resolves the issue by:
1. Rejecting duplicate responses early in `ParseResponse` if a response
   has already been received and saved for the query
   (`info.mSavedResponse != nullptr`), returning `kErrorDrop`.
2. Initializing the `mSavedResponse` field of the `QueryInfo` struct
   to `nullptr` before allocating the host resolution query (`newQuery`)
   to prevent it from inheriting a potentially non-null saved response
   from its parent.
This commit is contained in:
Jonathan Hui
2026-05-06 12:32:11 -07:00
committed by GitHub
parent 7e646d19dc
commit 02d000c747
+2
View File
@@ -1497,6 +1497,7 @@ Error Client::ParseResponse(const Message &aResponseMessage, Query *&aQuery, Err
VerifyOrExit(aQuery != nullptr, error = kErrorNotFound);
info.ReadFrom(*aQuery);
VerifyOrExit(info.mSavedResponse == nullptr, error = kErrorDrop);
queryName.SetFromMessage(*aQuery, kNameOffsetInQuery);
@@ -1872,6 +1873,7 @@ void Client::ResolveHostAddressIfNeeded(Query &aQuery, const Message &aResponseM
info.mMessageId = 0;
info.mTransmissionCount = 0;
info.mMainQuery = &FindMainQuery(aQuery);
info.mSavedResponse = nullptr;
SuccessOrExit(AllocateQuery(info, nullptr, hostName, newQuery));
IgnoreError(SendQuery(*newQuery, info, /* aUpdateTimer */ true));