Skip to content

testing: potential issues in runCleanup - infinite recursion on cancelCtx panic and incorrect nil check logic #76876

@dxasu

Description

@dxasu

Issue 1: Infinite recursion if c.cancelCtx()

if c.cancelCtx != nil {

If c.cancelCtx() panics and len(c.cleanups) > 0, the following happens:

  1. c.cancelCtx() panics
  2. The deferred function checks len(c.cleanups) > 0 → true
  3. Recursively calls runCleanup(normalPanic)
  4. c.cancelCtx is still not nil, calls c.cancelCtx() again
  5. Panics again → infinite recursion → stack overflow

Suggested fix:

if c.cancelCtx != nil {
    cancel := c.cancelCtx
    c.cancelCtx = nil  // clear before calling to prevent repeated calls
    cancel()
}

Issue 2: Incorrect nil check conflates empty slice with nil element

https://github.com/golang/go/blob/ad91f5d241f3b8e85dc866d0087c3f13af96ef33/src/testing/testing.go#L1655C1-L1668C3

The current logic cannot distinguish between:

  • Case A: c.cleanups is empty (cleanup stays default nil)
  • Case B: c.cleanups contains a nil function pointer

In Case B, returning early would skip remaining cleanup functions.

Suggested fix:

for {
    c.mu.Lock()
    if len(c.cleanups) == 0 {
        c.mu.Unlock()
        return nil
    }
    last := len(c.cleanups) - 1
    cleanup := c.cleanups[last]
    c.cleanups = c.cleanups[:last]
    c.mu.Unlock()
    
    if cleanup != nil {
        cleanup()
    }
    // continue to process remaining cleanups even if this one was nil
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.WaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions