Skip to content

httprespbodyclose: branch_state_merge gap still unfixed (issue 62115 auto-expired, 10th reverse-phantom) #63918

Closed

Description

@github-actions

Summary
Issue #62115 (this bugs prior filing, sg72a2) was auto-expired closed as not_planned, but the underlying bug in pkg/linters/httprespbodyclose/httprespbodyclose.go is 100 percent unchanged. This is the 10th confirmed reverse-phantom closure across this Sergo run history (a closed-not_planned issue whose code was never touched).

Bug
inspectFunc (line 61) keeps exactly one map[types.Object]*respVarState per function, populated by a single flat ast.Inspect over the whole function body (inspectNode, line 76). The map is not scoped to branches: if one arm of an if/else closes the response body manually and the sibling arm defers the close of the SAME variable, both writes land in the SAME respVarState entry. The final check (line 68) is hasManualClose and not hasDeferClose, so the sibling defer sets hasDeferClose=true and silently masks the manual-close arms genuine violation.

Example that is not flagged even though the manual-close branch leaks Body without a defer:

resp, err := http.Get(url)
if err != nil {
    return err
}
if fastPath {
    resp.Body.Close() // never deferred, should be flagged, is not
} else {
    defer resp.Body.Close()
}

Evidence

  • respVars is declared once per FuncDecl/FuncLit at line 61 and shared across the whole ast.Inspect at line 63.
  • markBodyClose (line 111) and the DeferStmt handler (line 94) both write into the same map entry keyed only by types.Object, with no branch or path awareness.
  • CI-enforced on both native and wasm (.github/workflows/cgo.yml lines 1487 and 1490 both include -httprespbodyclose), yet testdata has no if/else sibling-branch case.
  • Same root-cause class (branch_state_merge) already confirmed to affect the shared pkg/linters/internal/resourcetracker.go framework used by manualmutexunlock/fileclosenotdeferred/contextcancelnotdeferred (issue 62304, still open) - httprespbodyclose has its own standalone copy of the same design flaw.

Recommendation
Scope respVarState per control-flow branch, or track hasManualClose/hasDeferClose along an execution path rather than a flat per-object map, so a manual close in one branch cannot be masked by a defer in a sibling branch.

Effort: Medium, requires branch-aware state tracking, likely shareable with whatever fix lands for the resourcetracker framework.

Generated by 🤖 Sergo - Serena Go Expert · claude · agent · 165.9 AIC · ⌖ 8.61 AIC · ⊞ 6.9K · ◷

  • expires on Oct 4, 2026, 8:03 PM UTC-08:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    cookieIssue Monster Loves Cookies!sergo

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions