Repository navigation
Conversation
With no keys the positional index has fields of 0 bits and no word to hold them, yet LogSequence touched word 0. RPFC and RPHTFC also wrote past their bucket beginnings, the search for a string gave bucket center-1 from center 0, and the search for a prefix read bucket 1. A field of no bits now reads as 0, the search gives NORESULT, and an empty range ends the prefix search.
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.
Problem
A dictionary built with no keys reads past a heap array when it is built. A query on it then crashes.
The over-read is silent in a plain run: build, save, load,
extractandextractTableexit 0. It shows only under AddressSanitizer. The queries are not silent. In a plain runlocateaborts, andlocatePrefixandextractPrefixgive SIGSEGV.The
Buildtool refuses an empty key file (Error: malloc failed, fromRePair/Coder/basics.cpp:38), so this is reached through the library, from an iterator with no keys.Reproducer
master at 568bf8a, all five front-coded structures (HTFC, HHTFC, RPFC and RPHTFC build a PFC first):
clang 22.1.8 with
-fsanitize=address, at the first line:Cause
Two separate causes, one input:
StringDictionaryPFC.cpp:122builds it withbits(bytesStrings), which isbits(0)= 0.LogSequencethen has fields of 0 bits andnumElementsFor(0, 1)= 0 words, andsetFieldandgetFieldstill go througharray[0]:set_fieldandget_fieldinutils/LogSequence.h, called atutils/LogSequence.cpp:98and:81.locateBucketstarts withcenter = 0and never enters its loop, becauseright = buckets = 0. It then looks at bucketcenter-1, which wraps toSIZE_MAX, andgetFieldthrows.locatePrefixandextractPrefixget no bucket range and read a bucket anyway.center = 0inlocateBucket:410:628:687:505:630leftBucket > NORESULTin prefix search:226:415:468:319:410Fix
LogSequence: a field of 0 bits holds 0, sogetFieldreturns 0 andsetFieldwrites nothing. It is fixed inLogSequence, not at the caller, so the next caller with 0 bits is safe too. The rejected alternative wasmax(bits(bytesStrings),1)atStringDictionaryPFC.cpp:122,StringDictionaryHTFC.cpp:300andStringDictionaryHHTFC.cpp:352. It moves the saved bytes of a zero-key file and leavesLogSequenceas it is.locateBucketstarts atcenter = 1. It changes nothing with buckets, because the loop setscenterfirst.buckets > 0.StringDictionaryRPFC.cpp:159andStringDictionaryRPHTFC.cpp:172takebeginnings[buckets] = ibytes;, which is the change in Fix write past the bucket beginnings in RPFC and RPHTFC #5, since zero keys write index 1 of a 1-entry vector there.Validation
tests/zero_keys.cpp: all five structures at buckets 2 and 8, built, saved and loaded. It expectsnumElements0,locateNORESULT,extractNULLwith length 0,extractTableandlocatePrefixwith no next, andextractPrefixNULL. The repository has no test framework, so this is a standalone program with atests/%rule in theMakefile.-fsanitize=addressterminate called, SIGABRTheap-buffer-overflowutils/LogSequence.cppback at masterokheap-buffer-overflowStringDictionary{PFC,HTFC,HHTFC,RPFC,RPHTFC}.cppback at masterterminate called, SIGABRTterminate calledorheap-buffer-overflowokokThe
LogSequencerow is the plain over-read: the test passes there in a plain build, so that half fails only under ASan.Not run:
libcds/tests,Test r, GCC's ASan (its library is not on this machine), compilers older than GCC 16, and a zero-key file written by master and read by this branch. Those files arecmpidentical to this branch's.Compatibility
No saved byte moves. Zero-key files equal master's by
cmpin all five structures at bucket 8. On 2,048 and 100,000 keys, all five structures at buckets 2 and 8, all 20 files equal master's.LogSequencechanges only wherenumbitsis 0.Conflicts:
git merge-treemerges this branch cleanly with each of #5 to #11 and #12. It carries the identical line of #5 in two files, and git merges that without a conflict, so either can land first. The merge of all nine passes all nine tests, plain and under ASan.