Assert adb find loop-affinity invariant at lifetime entry points

The dns_adbfind_t lifetime model has no reference counting; storage
liveness is held together by find->lock and the FIND_EVENT_SENT
idempotency flag, plus an unwritten cross-module rule that all
non-trivial operations on a find run on find->loop. If a caller
violates that rule, the unlock-relock window in dns_adb_cancelfind
(and similar paths) becomes a use-after-free and we crash later
inside libpthread on a corrupted mutex.

Add REQUIREs at dns_adb_cancelfind, dns_adb_destroyfind and
find_sendevent so a violation aborts at the offending call site
rather than silently freeing storage another loop is still touching.
Also poison find->magic with ~DNS_ADBFIND_MAGIC in free_adbfind so
DNS_ADBFIND_VALID catches reuse-after-free at the next public entry
point instead of letting the dangling pointer reach the mutex code.

Assisted-by: Claude:claude-opus-4-7
This commit is contained in:
Ondřej Surý 2026-05-01 06:44:06 +02:00
parent 7b87ab0236
commit 2d468cb21f

View file

@ -1084,7 +1084,7 @@ free_adbfind(dns_adbfind_t **findp) {
REQUIRE(!ISC_LINK_LINKED(find, plink));
REQUIRE(find->adbname == NULL);
find->magic = 0;
find->magic = ~DNS_ADBFIND_MAGIC;
isc_mutex_destroy(&find->lock);
@ -2153,6 +2153,8 @@ dns_adb_destroyfind(dns_adbfind_t **findp) {
DP(DEF_LEVEL, "dns_adb_destroyfind on find %p", find);
REQUIRE(find->loop == NULL || isc_loop() == find->loop);
adb = find->adb;
LOCK(&find->lock);
@ -2178,6 +2180,8 @@ dns_adb_destroyfind(dns_adbfind_t **findp) {
*/
static void
find_sendevent(dns_adbfind_t *find) {
REQUIRE(find->loop != NULL && isc_loop() == find->loop);
if (!FIND_EVENTSENT(find)) {
atomic_store(&find->status, DNS_ADB_CANCELED);
@ -2195,6 +2199,7 @@ dns_adb_cancelfind(dns_adbfind_t *find) {
REQUIRE(DNS_ADBFIND_VALID(find));
REQUIRE(DNS_ADB_VALID(find->adb));
REQUIRE(find->loop != NULL && isc_loop() == find->loop);
LOCK(&find->lock);
REQUIRE(FIND_WANTEVENT(find));