Skip to content

robustness: cache mutates shared DNSRecord instances in place to expire TTL #1780

Description

@bluetoothbot

Problem

async_mark_unique_records_older_than_1s_to_expire walks the cache and calls _async_set_created_ttl(record, now, 1), which mutates record.created and record.ttl in place via record._set_created_ttl(...), then re-_async_adds the same object. The code itself flags this — line 345 says "It would be better if we made a copy instead of mutating the record in place, but records currently don't have a copy method." Listeners (RecordUpdate.old references obtained from cache.async_get_unique elsewhere in the same dispatch tick), ServiceInfo instances holding references to cached records, and anything in _handlers/multicast_outgoing_queue.AnswerGroup.answers that keys on a DNSAddress/DNSPointer instance will silently see their TTL/created flip to the new values mid-dispatch. The RFC-mandated "expire in 1s" path (RFC 6762 §10.2) is therefore not isolated from in-flight consumers.

Why This Matters

Subtle correctness drift in hot path: any code path that captured a DNSRecord reference and decided "this record is fresh, use it" can have the same object turn stale between two of its own lines. It's also hostile to free-threading (3.14t) — concurrent reads of record.created / record.ttl while another thread is in the middle of _set_created_ttl are an unsynchronized write race.

Suggested Fix

Add a copy_with_ttl(now, ttl) (or _cdef-typed _clone_with_created_ttl) method on DNSRecord (and update the .pxd so the new method is visible on the cython hot path), then in _async_set_created_ttl replace the in-place mutation with replacement = record.copy_with_ttl(now, 1); self._async_add(replacement). The old record will fall out of the cache when the new one supersedes it via async_add_records. Update the comment to remove the "It would be better" footnote once done.

Details

Severity 🟡 Medium
Category robustness
Location src/zeroconf/_cache.py:325-348
Effort 🛠️ Moderate effort

🤖 Created by Kōan from audit session

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions