Skip to content

Bug: valet-dns: Race condition and overly broad resolv.conf scanning can cause file corruption #456

@scottzirkel

Description

@scottzirkel

Basic info
┌────────────────────────────┬─────────────┬───────────────┐
│ Distro (Name and version) │ PHP Version │ Valet version │
├────────────────────────────┼─────────────┼───────────────┤
│ Arch Linux (kernel 6.18.3) │ 8.5.1 │ 2.4.4 │
└────────────────────────────┴─────────────┴───────────────┘

  • I've checked the issue queue and could not find anything similar to my bug.
  • I'm on the latest version of valet-linux (valet --version): 2.4.4
  • I've run valet fix and valet install after updating and before submitting my
    issue/feature.

What is the problem?

The valet-dns script has two bugs that can cause /opt/valet-linux/resolv.conf to become
corrupted:

  1. Race condition: The inotifywait loop triggers updateNameservers() on every file
    change. During rapid changes (boot, network transitions, crash recovery), multiple calls
    run concurrently, causing interleaved/repeated writes.
  2. Overly broad file scanning: The getFiles() function uses find to scan for resolv.conf
    files, picking up unintended locations like Docker containers, btrfs snapshots, flatpak
    runtimes, and overlay filesystems. Also, line 79 has a syntax issue - -path expects a
    pattern, not !:
    find ${DIR} -path ! -readable -prune -o -name 'resolv.conf' -print

What was supposed to happen?

The resolv.conf file should contain clean DNS options and nameserver entries, with no
duplicated or corrupted content.

What actually happened?

After a hard system crash, /opt/valet-linux/resolv.conf grew to 150KB with a single 76KB
line of repeated DNS options:
options trust-ad ndots:0 edns0 trust-ad ndots:0 edns0 trust-ad ndots:0 ...

This broke Docker with the error bufio.Scanner: token too long (Docker has a 64KB line
buffer limit).

How to reproduce this?

  1. Have a system with Docker containers, btrfs snapshots, or flatpak installed (all
    contain resolv.conf files)
  2. Experience rapid DNS config changes or a hard crash during valet-dns activity
  3. Check /opt/valet-linux/resolv.conf for corrupted/repeated content

What is the solution?

  1. Add flock to prevent concurrent writes:
function updateNameservers() {
    (
        flock -n 200 || return 1
        # ... existing logic ...
    ) 200>"${WORKDIR}/update.lock"
}
  1. Replace broad find with explicit safe paths:
function getDirs() {
    DIRS=()
    [[ -d "/run/systemd/resolve" ]] && DIRS+=("/run/systemd/resolve")
    [[ -d "/run/NetworkManager" ]] && DIRS+=("/run/NetworkManager")
    [[ -d "/run/resolvconf" ]] && DIRS+=("/run/resolvconf")
    [[ -d "/run/connman" ]] && DIRS+=("/run/connman")
}

function getFiles() {
    FILES=()
    [[ -f "/run/systemd/resolve/resolv.conf" ]] &&
FILES+=("/run/systemd/resolve/resolv.conf")
    [[ -f "/run/systemd/resolve/stub-resolv.conf" ]] &&
FILES+=("/run/systemd/resolve/stub-resolv.conf")
    [[ -f "/run/NetworkManager/resolv.conf" ]] &&
FILES+=("/run/NetworkManager/resolv.conf")
    [[ -f "/run/NetworkManager/no-stub-resolv.conf" ]] &&
FILES+=("/run/NetworkManager/no-stub-resolv.conf")
    [[ -f "/run/resolvconf/resolv.conf" ]] && FILES+=("/run/resolvconf/resolv.conf")
    [[ -f "/run/connman/resolv.conf" ]] && FILES+=("/run/connman/resolv.conf")

    if [[ -d "/run/resolvconf/interface" ]]; then
        for f in /run/resolvconf/interface/*; do
            [[ -f "$f" ]] && FILES+=("$f")
        done
    fi
}

Can create PR, just didn't know how you wanted it handled.

Sources

  • Docker "bufio.Scanner: token too long" error: occurs when any line exceeds 64KB
  • The corruption pattern suggests concurrent writes from multiple updateNameservers()
    invocations

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions