Skip to content

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

@bluetoothbot

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions