utils, random: fix slice(), and make the TESTING RNG unbuildable in release - #407
Open
xanimo wants to merge 1 commit into
Open
utils, random: fix slice(), and make the TESTING RNG unbuildable in release#407xanimo wants to merge 1 commit into
xanimo wants to merge 1 commit into
Conversation
…elease
Two findings from an audit sweep, unrelated except that both are guards
that were not doing what they appeared to do.
slice() is LIBDOGECOIN_API and had no in-tree callers, which is why
neither of its defects was noticed:
strncpy(result, str + start, end - start);
strncpy writes no terminator when it copies its full count, and the count
here was always exactly the number of bytes copied -- so result came back
unterminated for every input, and a caller printing it read past the
buffer. Separately, `end - start` is size_t arithmetic, so end < start
wrapped to a length near SIZE_MAX and strncpy ran until it hit the source
NUL or faulted.
Now measures the source, clamps end to it, rejects end <= start and a
start past the end, copies with memcpy and terminates. The header records
the contract the function never stated: result needs (end - start) + 1
bytes, since the signature carries no size for it.
Verified the test catches the original rather than merely passing with
the fix: with slice() reverted, test_utils_slice fails at the first
assertion with
Expect: cde
Receive: cdeXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX...
which is the unterminated buffer.
Separately, src/random.c carries a TESTING branch that replaces the
CSPRNG with srand(time(NULL)) and rand(). Nothing in the build system
defines TESTING -- not CMakeLists.txt, not configure.ac -- so it is
unreachable today, and this does not change that. It adds an #error when
TESTING is combined with NDEBUG, so that a release build which somehow
enabled it fails loudly at compile time instead of quietly generating
keys guessable from the wall clock. A dormant path that swaps out the RNG
should be impossible to ship, not merely unlikely to be reached.
82/82, clean under ASan.
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.
Two findings from the audit sweep. Unrelated to each other except in kind: both are guards that looked like they were doing something and weren't.
slice()— unterminated, and underflowsLIBDOGECOIN_API, with no in-tree callers, which is why neither defect was noticed:It never terminated.
strncpywrites no NUL when it copies its full count — and the count here is always exactly the number of bytes copied. Soresultcame back unterminated for every input, and a caller doingslice(s, buf, 0, 3); printf("%s", buf);reads past the buffer.And
end < startunderflows.end - startissize_t, so it wraps to a length nearSIZE_MAXandstrncpyruns until it hits the source NUL or faults.Now it measures the source, clamps
endto it, rejectsend <= startand astartpast the end, copies withmemcpy, and terminates. The header records the contract the function never stated:resultneeds(end - start) + 1bytes, since the signature carries no size for it.Verified, not assumed
With
slice()reverted to the original one-liner, the new test fails at the first assertion:That trailing
XXXX…is the buffer's prior contents — the missing terminator, made visible.Tests cover: an ordinary slice, the whole string,
end < start,end == start,startpast the end,endpast the end, and NULL arguments.The
TESTINGRNG branchsrc/random.ccarries a branch that replaces the CSPRNG withsrand(time(NULL))andrand().Nothing in the build system defines
TESTING— notCMakeLists.txt, notconfigure.ac. It's unreachable today, and this PR doesn't change that. What it adds is an#errorwhenTESTINGis combined withNDEBUG, so a release build that somehow enabled it fails loudly at compile time rather than quietly generating keys guessable from the wall clock.A dormant path that swaps out the RNG should be impossible to ship, not merely unlikely to be reached. I've left the branch in place rather than deleting it, in case something out-of-tree uses it — deleting it is also defensible and I'm happy to do that instead if you'd prefer.
82/82, clean under ASan.
Note this will show red on cppcheck until #403 lands — that job fails on
0.1.5-devitself, not on this branch.