Skip to content

id(serve): is_process_alive answers 'alive' for pid 0 and u32::MAX — a lock file with either pid is never stale and wedges serve discovery #2142

Closed

Description

@dezren39

Problem

get_serve_info() decides whether the serve lock (.iroh-serve.lock, pkgs/id/src/lib.rs:311) is stale by calling is_process_alive(info.pid), where pid comes straight from the lock file's JSON (ServeInfo.pid: u32, pkgs/id/src/commands/serve.rs:130-141). is_process_alive (serve.rs:196-210) casts that u32 to i32 and calls libc::kill(pid as i32, 0), with a comment claiming the cast is safe because "valid PIDs on Unix are always positive and fit in i32". That holds for a pid written by create_serve_lock (std::process::id()), but not for the pid read back from disk: serde accepts any u32, and two values make the stale check answer "alive" for no process at all:

  • pid: 0 → kill(0, 0) checks the caller's own process group — it succeeds whenever the calling process exists, i.e. always.
  • pid: 4294967295 (u32::MAX) → casts to -1 → kill(-1, 0) succeeds if any process the caller may signal exists, i.e. always.

A lock file carrying either value is therefore never treated as stale: get_serve_info returns the bogus ServeInfo and never deletes the lock (serve.rs:171-185). Every command that consults the lock first — get, put, list, peers, the REPL and the meta client (commands/get.rs:101, put.rs:153, list.rs:72, peers.rs:204, repl.rs:235, meta_client.rs:103) — then tries to reach a serve instance that does not exist instead of falling back to a local node / "no serve running".

Reachability is mundane, not adversarial: a hand-edited, corrupted-but-still-JSON-parseable, or partially migrated lock file (or any future writer that records a sentinel pid) permanently wedges serve discovery for that directory until a human deletes .iroh-serve.lock by hand.

Evidence (verified this run, main 8124cc0b)

Probe on the laptop (~/git/fleet/probe-2026-10-29-pidalive/), running is_process_alive copied verbatim from serve.rs:196-210:

own pid 3726549 -> true
pid 0 -> true
pid u32::MAX (4294967295, casts to -1) -> true
pid 4294967294 (casts to -2) -> false
dead pid 3727865 -> false        (spawned, reaped, then checked)
pid 999999 (likely dead) -> false

So ordinary dead pids are correctly reported dead; only the sentinel/wrapped values lie. Dedup: gh searches for is_process_alive, serve lock, and pid reuse returned no issue covering this (only closed #443, about gitignoring serve artifacts).

Why it matters

Serve discovery is the front door for most CLI commands in a served directory. A lock that can never go stale turns a recoverable state (dead serve) into a permanent, confusing failure (connection attempts to a dead/wrong endpoint), with the fix — deleting a dotfile — undiscoverable from any error message.

Acceptance criteria

  • is_process_alive (or get_serve_info) refuses non-positive / out-of-range pids: pid == 0 and any pid that does not fit a positive i32 are treated as "not alive" (stale lock), never passed to kill.
  • A lock file with pid: 0 or pid: 4294967295 is removed by get_serve_info and None is returned, exactly as for an ordinary dead pid.
  • A live pid and an ordinary dead pid behave exactly as before.
  • Regression tests cover pid 0, u32::MAX, a dead pid, and the caller's own pid.
  • The misleading cast-safety comment is corrected to state where the pid is validated.

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    priority/lowNice to have; do when convenient
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 29, 2026
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a, run 2026-10-29 01:05 CDT
    • Found via: filed this run
    • Task: Triage the serve-lock pid validation gap: pin whether the guard lives in is_process_alive or get_serve_info, then route a small-fix packet.
    • Expectation: effort/xs — done when pid 0 / out-of-i32-range pids are treated as stale, the lock is removed, and regression tests cover 0, u32::MAX, dead and live pids.
    • Links: probe ~/git/fleet/probe-2026-10-29-pidalive/ (laptop, verbatim is_process_alive, main 8124cc0)
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 1, 2026
  5. 130 remaining items

  6. added
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 2, 2026
  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1

  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up (resumed)

  9. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

    • Agent: queue worker (repo-worker-code), OpenChamber scheduled lane
    • Session / ID: queue task 5185
    • Reason: parked for 30 minutes because matching PR fix(id): reject invalid serve lock PIDs #4097 is open, non-draft, and Drew-owned; the hard stop forbids competing work.
    • Resume condition: explicit ownership handoff/release or PR closure/merge, followed by fresh issue and PR metadata checks.
    • Worktree / changes: none; no PR internals were inspected and no source files were changed.
  10. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 2, 2026
  11. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    kind: pickup
    task: 5185
    lane: c
    state: claimed
    note: Lane C picked up task 5185. Ownership correction on the record: the earlier Drew-owned park used the author+assignee=dezren39 heuristic. Fresh check 2026-11-02T12:17Z shows PR #4097 is on fleet branch queue/5185-2142-pid-guard (this task's own branch), and neither issue #2142 nor PR #4097 carries any hard-stop label (held, user/owned, do-not-merge, do-not-auto-edit). PR #4097 is the canonical task-bound PR; resuming from it: diff review, Codex findings, CI, merge when verified.

  12. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    kind: done
    task: 5185
    lane: c
    state: done (fixed_merged)
    note: PR #4097 merged as 3d6deea; issue auto-closed. Verification: diff matches triage task 3146's pinned fix exactly (guard inside is_process_alive, corrected cast comment, regression tests); Codex review on head 0002eb3 reported no findings; laptop cargo test is_process_alive: 5 passed including the new pid 0 and out-of-i32-range tests. PR CI was red for unrelated reasons: Magic Nix Cache rate-limit throttling (HTTP 418) in test-unit and pre-existing rustfmt drift in files this PR does not touch (retention_pruning.rs, cli_integration.rs).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions