Conversation
…quality guard
Under an "if len(s) == N" guard the *ssa.IndexAddr arm of the bounded
case suppressed the issue when the constant index equalled N. Valid
indices of a length-N slice are 0..N-1, so index == N is the one value
that is always out of range, and G602 silently dropped it.
Use isSliceIndexInsideBounds, as the sibling upperBounded arm already
does, and recover the asserted length when the compared expression
carries a constant offset ("len(s)-1 == 1" asserts a length of 2).
Fixes part of securego#1727
invBound maps bounded to itself, so both successors of "if len(s) == N" are walked with bound=bounded and value=N. Entering the else branch only proves the length is not N, yet the *ssa.IndexAddr arm cleared every constant index in [0, N) there. Restrict the suppression to the then successor (i == 0) and cover the inverted branch with samples. Fixes part of securego#1727
…y guards Adds samples exercising the code paths the equality-guard fix has to hold on but the existing table did not reach: the constant on the left of the guard, a positive constant offset on the compared expression, an "else if" chain and a nested guard inside the outer "else" branch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Addresses part of #1727 (the equality-guard case only; see "Not fixed here").
What was wrong
Under an
if len(s) == Nguard, thebounded/*ssa.IndexAddrarm inanalyzers/slice_bounds.gotreated a constant index equal toNas in-bounds and deleted the finding. For a slice of lengthNthe valid indices are0..N-1, sos[N]is the one index that is always out of range: the analyzer dropped exactly the case it exists to catch.The change
One arm, three small parts:
isSliceIndexInsideBounds(h, index)is what the siblingupperBoundedarm uses a few lines above; theboundedarm had a hand-rolledint(indexValue) == valueinstead.assertedLenrecovers the asserted length when the comparison carries a constant offset, soif len(s)-1 == 1 { s[1] }is still understood as a length of 2 and stays clean. Without it the tighter predicate would have turned that safe code into a false positive.thensuccessor (i == 0).invBoundmapsboundedto itself, so both successors of theifare walked with the same bound and value; entering theelsebranch only proves the length is notN, so nothing may be cleared there. Without this gate the tighter predicate would have cleared every constant index in[0, N)inside theelsebranch, a broader false negative than the one being fixed. That regression was reproduced against an earlier version of this change before the gate was added.Alternatives considered and rejected: fixing
invBound(feeds everyboundconsumer in the file, including the taint path) and teachingextractBinOpBoundto return the offset (four call sites); both widen the blast radius for a one-arm bug.Tests
New samples in
testutils/g602_samples.go, following the >= 2 positive / >= 2 negative convention: thethenbranch (index equal to the asserted length, offset guards, zero-length guard), theelsebranch (must keep reporting), reversed operands (N == len(s)), and nested equality guards. Each sample was checked to discriminate on its own: the positive ones fail onmaster, the negative ones stay green onmasterand on this branch.gofmtis clean on both touched files. Not run here:go test ./...andgolangci-lint(the sandbox this was developed in runs out of memory on the full module build); CI covers both.Not fixed here
#1727 reports three things. This PR claims only the equality-guard gap. The
appendcase (s = append(s, 10); if len(s) == 3 { s[3] }) still reports nothing with this change applied, so tracking is being lost acrossappendin a different part of the pass, and the>=vs==inconsistency is not touched either. Both are separate defects and belong in their own PRs. While testing this, one pre-existing false positive was also noticed in the neighbouring*ssa.Slicearm (s[0:3]underlen(s) == 3); it is not caused or changed by this PR and is left alone.AI assistance: the bug was found and the fix and samples were drafted with AI tooling in my workflow; the test runs above were executed as pasted. I'm responsible for the change and will handle review feedback.