Skip to content

[code-review] nil_deref_check clears ALL nil-risk variables on any error check — multi-error-variable false negatives #97

Description

@topcheer

File and Lines

internal/agent/nil_deref_check.go lines 183-206 (clearNilRiskOnErrorCheck)

Problem

When if err != nil is encountered, clearNilRiskOnErrorCheck clears ALL entries in the nilRisk map, regardless of which error variable was actually checked. This causes false negatives when multiple error variables exist and only some are checked.

Trigger Scenario

func example() {
    a, err1 := getA()   // "a" marked as nil-risk (associated with err1)
    b, err2 := getB()   // "b" marked as nil-risk (associated with err2)

    if err1 != nil {     // checks err1, but clears BOTH "a" and "b"
        return
    }

    // err2 was NEVER checked, so b could still be nil
    fmt.Println(b.Field) // FALSE NEGATIVE: b dereferenced but not flagged
}

Execution trace:

  1. After a, err1 := getA(): nilRisk = {"a": pos}
  2. After b, err2 := getB(): nilRisk = {"a": pos, "b": pos}
  3. At if err1 != nil: clearNilRiskOnErrorCheck deletes ALL entries
  4. At fmt.Println(b.Field): detectNilDeref finds no entry for "b" → no warning

Expected vs Actual Behavior

  • Expected: Only variables associated with err1 should be cleared when err1 is checked. Variables associated with err2 should remain tracked.
  • Actual: ALL nil-risk variables are cleared regardless of which error variable is checked.

The comment at lines 200-201 says "we only clear those whose position is before this if-statement, but since ast.Inspect visits in order, all existing entries qualify" — but the real issue is that clearing is not specific to the error variable being checked. The nilRisk map (map[string]int) tracks variable name → position but does not track which error variable each non-error variable is associated with.

Severity

HIGH — Silent false negatives for dangerous nil-pointer dereference patterns. Multi-error-variable patterns are common in non-trivial Go code.

Fix Suggestion

Change nilRisk to track the association between non-error variables and their error variable (e.g., map[string]string for varName → errVarName). Then clearNilRiskOnErrorCheck should only clear entries associated with the specific error variable being checked.

Activity

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions