headscale/hscontrol/mapper
Kristoffer Dalby b09af3846b hscontrol/poll,state: fix grace period disconnect TOCTOU race
When a node disconnects, serveLongPoll defers a cleanup that starts a
grace period goroutine. This goroutine polls batcher.IsConnected() and,
if the node has not reconnected within ~10 seconds, calls
state.Disconnect() to mark it offline. A TOCTOU race exists: the node
can reconnect (calling Connect()) between the IsConnected check and
the Disconnect() call, causing the stale Disconnect() to overwrite
the new session's online status.

Fix with a monotonic per-node generation counter:

- State.Connect() increments the counter and returns the current
  generation alongside the change list.
- State.Disconnect() accepts the generation from the caller and
  rejects the call if a newer generation exists, making stale
  disconnects from old sessions a no-op.
- serveLongPoll captures the generation at Connect() time and passes
  it to Disconnect() in the deferred cleanup.
- RemoveNode's return value is now checked: if another session already
  owns the batcher slot (reconnect happened), the old session skips
  the grace period entirely.

Update batcher_test.go to track per-node connect generations and
pass them through to Disconnect(), matching production behavior.

Fixes the following test failures:
- server_state_online_after_reconnect_within_grace
- update_history_no_false_offline
- nodestore_correct_after_rapid_reconnect
- rapid_reconnect_peer_never_sees_offline
2026-03-19 07:05:58 +01:00
..
batcher.go mapper/batcher: replace connected map with per-node disconnectedAt 2026-03-16 02:22:56 -07:00
batcher_bench_test.go mapper/batcher: replace connected map with per-node disconnectedAt 2026-03-16 02:22:56 -07:00
batcher_concurrency_test.go mapper/batcher: serialize per-node work to prevent out-of-order delivery 2026-03-19 07:05:58 +01:00
batcher_scale_bench_test.go mapper/batcher: replace connected map with per-node disconnectedAt 2026-03-16 02:22:56 -07:00
batcher_test.go hscontrol/poll,state: fix grace period disconnect TOCTOU race 2026-03-19 07:05:58 +01:00
batcher_unit_test.go mapper/batcher: replace connected map with per-node disconnectedAt 2026-03-16 02:22:56 -07:00
builder.go all: fix golangci-lint issues (#3064) 2026-02-06 21:45:32 +01:00
builder_test.go all: fix golangci-lint issues (#3064) 2026-02-06 21:45:32 +01:00
mapper.go mapper: remove Batcher interface, rename to Batcher struct 2026-03-14 02:52:28 -07:00
mapper_test.go all: upgrade to Go 1.26rc2 and modernize codebase 2026-02-08 12:35:23 +01:00
node_conn.go mapper/batcher: replace connected map with per-node disconnectedAt 2026-03-16 02:22:56 -07:00
tail_test.go all: upgrade to Go 1.26rc2 and modernize codebase 2026-02-08 12:35:23 +01:00