Problem
Server.Start and Client.initLocked allocate several resources (netMon, the WireGuard engine, and the gVisor netstack) before assigning s.lb / c.lb. If any intermediate step fails, Server.Close / Client.Close see a nil backend and return early, so the already-created resources leak. In Client.ensureStarted, a failing lb.Start() also leaves a broken backend behind instead of retrying cleanly on the next call.
This is the underlying issue that PR #11 fixes.
Reproduction
I added a small test hook plus TestServerStartCleansUpOnFailure to simulate lb.Start() failing after Server.Start has already assigned s.lb. On main (commit c04c5afee) the test fails because s.lb is leaked:
func TestServerStartCleansUpOnFailure(t *testing.T) {
dm := integration.RunDERPAndSTUN(t, mkLogger(t, "derpstun"), "127.0.0.1")
reg := dm.Regions[1]
if reg == nil {
t.Fatal("no region 1 in derpmap")
}
SetStartBackendHookForTest(func(*locoBackend) error {
return errors.New("injected backend start failure")
})
defer SetStartBackendHookForTest(nil)
s := &Server{Logf: logger.Discard, Region: reg}
if err := s.Start(); err == nil {
t.Fatal("expected Server.Start to fail")
}
if s.lb != nil {
t.Fatal("Server.Start leaked a locoBackend after failure")
}
}
Test run on main:
=== RUN TestServerStartCleansUpOnFailure
tailcat_test.go:468: Server.Start leaked a locoBackend after failure
--- FAIL: TestServerStartCleansUpOnFailure (0.01s)
Verification that PR #11 fixes it
Checking out PR #11 (commit 9d20738db) and running the same test:
=== RUN TestServerStartCleansUpOnFailure
--- PASS: TestServerStartCleansUpOnFailure (0.01s)
Suggested fix
Apply the cleanup from PR #11:
- Use named return values in
Server.Start / Client.initLocked.
- Add deferred cleanup that closes
netMon, the engine, and netstack if startup returns an error.
- Reset
s.lb = nil in Server.Start on failure.
- In
Client.ensureStarted, close and clear c.lb when lb.Start() fails so the next call retries from a clean state.
References
Problem
Server.StartandClient.initLockedallocate several resources (netMon, the WireGuard engine, and the gVisor netstack) before assignings.lb/c.lb. If any intermediate step fails,Server.Close/Client.Closesee a nil backend and return early, so the already-created resources leak. InClient.ensureStarted, a failinglb.Start()also leaves a broken backend behind instead of retrying cleanly on the next call.This is the underlying issue that PR #11 fixes.
Reproduction
I added a small test hook plus
TestServerStartCleansUpOnFailureto simulatelb.Start()failing afterServer.Starthas already assigneds.lb. Onmain(commitc04c5afee) the test fails becauses.lbis leaked:Test run on
main:Verification that PR #11 fixes it
Checking out PR #11 (commit
9d20738db) and running the same test:Suggested fix
Apply the cleanup from PR #11:
Server.Start/Client.initLocked.netMon, the engine, andnetstackif startup returns an error.s.lb = nilinServer.Starton failure.Client.ensureStarted, close and clearc.lbwhenlb.Start()fails so the next call retries from a clean state.References
main@c04c5afee9d20738db