Repository navigation
Fix memory-safety and undefined-behavior bugs found with sanitizers - #39
Open
Varun Bhandari (varunbhandarii) wants to merge 1 commit into
Open
Varun Bhandari (varunbhandarii) wants to merge 1 commit into
Varun Bhandari (varunbhandarii) wants to merge 1 commit into
Conversation
Found while running a randomized differential test of ALEX under AddressSanitizer, UndefinedBehaviorSanitizer and ThreadSanitizer. Each fix comes with a test that fails under the sanitizers without it. - get_next_filled_position(): find_lower() and find_upper() can pass pos == data_capacity_. When the capacity is a multiple of 64, reading bitmap_[pos >> 6] is one word past the bitmap (ASan: heap-buffer-overflow). Return data_capacity_ early. Test: TestNextFilledPositionAtCapacity. - extract_rightmost_one(): negating the word as int64_t overflows when only the top bit is set (UBSan). Negate as uint64_t, which gives the same bits. Test: TestExtractRightmostOne. - find_best_fanout_existing_node() cast data nodes to AlexDataNode<T, P> with default template arguments, so an Alex with allow_duplicates = false (or another allocator) accessed its nodes through the wrong type (UBSan: invalid downcast). It now takes the instance's data node type. Test: TestInsertWithoutDuplicates. - delete_node() downcast each node again after destroying it, which is undefined. Cast once, before destroying. - Tests that build data nodes by hand allocated their arrays with new[], but nodes free them through std::allocator with bitmap_size_ words (ASan: alloc-dealloc-mismatch). Allocate with std::allocator and set bitmap_size_.
|
已收到,感谢来信。祝好!
|
Author
|
@microsoft-github-policy-service agree |
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.
While integrating ALEX into a transactional database
(learned-index-mako), a randomized
differential test run under AddressSanitizer, UndefinedBehaviorSanitizer and
ThreadSanitizer found the problems below. Each fix comes with a regression test that fails
under the sanitizers without it, and all existing tests still pass.
find_lower()/find_upper()can passpos == data_capacity_toget_next_filled_position(), which then readsbitmap_[pos >> 6]: one word past the bitmap when the capacity is a multiple of 64data_capacity_earlyextract_rightmost_one()negates the word asint64_t, which overflows when only the top bit is setuint64_t; same bitsfind_best_fanout_existing_node()casts nodes toAlexDataNode<T, P>with default template arguments, so anAlexwithallow_duplicates = false(or another allocator) uses its nodes through the wrong type when splittingdelete_node()downcasts each node again after destroying itnew[], but nodes free them throughstd::allocatorstd::allocator; setbitmap_size_Testing
With
-fsanitize=address,undefined, the unit tests (45 test cases, 192,499 assertions)pass with no sanitizer reports. Before this change, the existing tests produced 47 UBSan
and 12 ASan reports.
With the changes to
src/corereverted, each new test fails under the sanitizers:TestNextFilledPositionAtCapacity(heap-buffer-overflow),TestExtractRightmostOne(signed overflow) and
TestInsertWithoutDuplicates(invalid downcast).The normal CMake build (
-O3 -march=native) andctestpass.GCC 12, Ubuntu 22.04. Sanitizer build:
g++ -std=c++14 -O1 -g -march=native -fsanitize=address,undefined -fno-omit-frame-pointer \ -Isrc/core -I<doctest dir> test/unittest_main.cpp -o test_alex && ./test_alexSeparately: lookups update per-node statistics (
num_lookups_,num_exp_search_iterations_), so concurrent readers race even under a shared lock. I cansend a small opt-out macro for that as a separate PR if it would be useful.