Skip to content

test(leak): grow stack dump dynamically and cover wedged pipeline paths - #29

Merged
hammadmajid merged 1 commit into
mainfrom
fix/19-leak-detector
Sep 5, 2026
Merged

hammadmajid merged 1 commit into
mainfrom
fix/19-leak-detector

Conversation

@hammadmajid

Copy link
Copy Markdown
Contributor

Description

Fixes the silent stack truncation vulnerability in the goroutine leak detector and adds coverage for wedged pipeline and aggregator paths that were previously untested.

Closes #19

Type of Change

[✓] Bug fix (non-breaking change fixing an issue)
[ ] New feature (non-breaking change adding functionality)
[ ] Performance improvement
[ ] Refactoring or code cleanup
[ ] Documentation update
[ ] CI/CD or build workflow change

Key Changes

Growable Stack Dump: runtime.Stack silently truncates when the dump exceeds the buffer capacity, returning len(buf) with no error. A test binary with many live goroutines can easily exceed the previous fixed 128 KiB buffer, causing verifyNoGoroutineLeaks to pass vacuously.

  • Added fullStackDump() which dynamically doubles the buffer starting from 64 KiB until n < len(buf), guaranteeing the complete stack dump is captured without truncation.
  • Factored out grgGoroutinesInDump(raw string) []string and filtered internal testing / leak-checking stack frames (fullStackDump, grgGoroutinesInDump, awaitNoGRGGoroutines, requireNoLingeringGoroutines).
  • Added requireNoLingeringGoroutines(t, what) to assert no grg goroutines remain mid-test while holding an uncancelled context (proving the component self-unwound rather than relying on deferred cancellation).

Expanded Test Coverage:

  • TestLeakDetector_GrowsBufferUntilDumpFits: Proves that when 64 goroutines are parked at depth 64, the dump exceeds 128 KiB and the fixed buffer underreports the count (17 of 64), while fullStackDump() captures all 64.
  • TestGoroutineLeak_SearchPipeline_AbandonWithoutDraining: Exercises abandon without cancel by reading 1 result from ExecuteContext, abandoning without draining until workers park on resultsCh <- res, then cancelling and asserting all goroutines unwind.
  • TestGoroutineLeak_SearchPipeline_FatalErrorAbandonsRun: Asserts that a fatal error halts dispatch and stops workers without searching all remaining blobs or leaking goroutines.
  • TestGoroutineLeak_SearchPipeline_PanickingTask: Asserts panic containment in a task surfaces as an error from Execute without leaking goroutines.
  • TestGoroutineLeak_AggregateChannel_RealPipelineFatalError: Tests AggregateChannel driven by a real pipeline where a poisoned blob explodes while the results buffer is full and workers are blocked on sends. Asserts AggregateChannel draining resultsCh successfully unwinds the pipeline.
  • TestGoroutineLeak_AggregateChannel_RealPipelineCancelled: Tests AggregateChannel driven by a real pipeline with mid-stream context cancellation, asserting early termination, correct error propagation, and clean pipeline rundown.

Verification & Testing

[✓] Ran go test -v -count=1 ./...
[✓] Ran go test -race -shuffle=on -count=1 ./...
[✓] Ran go vet ./...
[✓] Added or updated unit/integration tests
[✓] Tested manually against sample Git repository histories

  go build ./...                                      clean
  go vet ./...                                        clean
  gofmt -l                                            empty
  go test -race -shuffle=on -count=1 ./test/...       ok
  go test -race -shuffle=on -count=1 ./...            ok

Checklist

[✓] gofmt clean
[✓] Every new test mutation-checked: reverting fixes makes them fail
[✓] Passes the full race suite with -shuffle=on

- Grow buffer dynamically in fullStackDump until strictly shorter than buffer
  to prevent silent truncation in runtime.Stack from masking leaks
- Filter internal leak check frames in grgGoroutinesInDump
- Add requireNoLingeringGoroutines to assert self-unwinding pipelines mid-test
- Add TestLeakDetector_GrowsBufferUntilDumpFits proving truncation avoidance
- Add TestGoroutineLeak_SearchPipeline_AbandonWithoutDraining
- Add TestGoroutineLeak_SearchPipeline_FatalErrorAbandonsRun
- Add TestGoroutineLeak_SearchPipeline_PanickingTask
- Add TestGoroutineLeak_AggregateChannel_RealPipelineFatalError
- Add TestGoroutineLeak_AggregateChannel_RealPipelineCancelled

Closes #19
@hammadmajid
hammadmajid merged commit 03709b7 into main Sep 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: goroutine-leak detector truncates silently and misses the paths that actually leak

1 participant