mirror of
https://github.com/savoirfairelinux/opendht.git
synced 2026-11-08 04:21:51 +00:00
Prefix::size_ counts bits, but commonBits walked content_ a byte at a time up to it. Two prefixes that agree on their whole common length are therefore read well past their end, which AddressSanitizer reports as a heap-buffer-overflow, and the count that comes back is eight times too large. A lookup with a common prefix length hits this as soon as an index entry matches the key it is looking for, and entry.prefix comes straight off the network. The flags were indexed the same way, so a partially known prefix was read at the wrong position too. swapBit had the other half of the confusion: it flipped bit (8 - bit) % 8 of the byte, while isActiveBit reads bit 7 - (bit % 8). getSibling, which flips the last bit of a prefix to name the sibling node in the trie, therefore returned a prefix differing in some other bit and sharing the last one, so lookup and insertion looked up the wrong node. Splitting a leaf and finding a canary both rely on it. Add tests covering both, which fail before the fix: commonBits returns 193 for two identical 160 bit prefixes, and a prefix and its sibling share 7 bits rather than all but the last. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
59 lines
2.0 KiB
C++
59 lines
2.0 KiB
C++
// Copyright (c) 2014-2026 Savoir-faire Linux Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "test_pht.h"
|
|
|
|
// opendht
|
|
#include "opendht/indexation/pht.h"
|
|
|
|
namespace test {
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(PhtTester);
|
|
|
|
using dht::indexation::Prefix;
|
|
|
|
void
|
|
PhtTester::testCommonBits()
|
|
{
|
|
// Prefix::size_ counts bits, so two prefixes that agree everywhere have
|
|
// as many bits in common as they are long, and no more.
|
|
const Prefix same {dht::Blob(20, 0xAB)};
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) 160, Prefix::commonBits(same, Prefix {dht::Blob(20, 0xAB)}));
|
|
|
|
// A difference in the very last bit leaves every other bit in common.
|
|
dht::Blob other(20, 0xAB);
|
|
other[19] = 0xAA;
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) 159, Prefix::commonBits(same, Prefix {other}));
|
|
|
|
// A difference in the very first bit leaves nothing in common.
|
|
dht::Blob first(20, 0xAB);
|
|
first[0] = 0x2B;
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) 0, Prefix::commonBits(same, Prefix {first}));
|
|
|
|
// Bits beyond the shorter prefix are not compared, whatever the content
|
|
// they are cut from holds.
|
|
const Prefix shorter = same.getPrefix(3);
|
|
CPPUNIT_ASSERT_EQUAL((size_t) 3, shorter.size_);
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) 3, Prefix::commonBits(shorter, same));
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) 3, Prefix::commonBits(same, shorter));
|
|
}
|
|
|
|
void
|
|
PhtTester::testSibling()
|
|
{
|
|
// The sibling of a node in the trie is the prefix that differs from it in
|
|
// its last bit, and in that bit only.
|
|
for (size_t length : {1, 7, 8, 9, 15, 16, 159}) {
|
|
const Prefix prefix = Prefix {dht::Blob(20, 0xAB)}.getPrefix(length);
|
|
const Prefix sibling = prefix.getSibling();
|
|
|
|
CPPUNIT_ASSERT_EQUAL(prefix.size_, sibling.size_);
|
|
CPPUNIT_ASSERT_EQUAL((unsigned) (length - 1), Prefix::commonBits(prefix, sibling));
|
|
CPPUNIT_ASSERT(prefix.isContentBitActive(length - 1) != sibling.isContentBitActive(length - 1));
|
|
|
|
// Being siblings is symmetric.
|
|
CPPUNIT_ASSERT(sibling.getSibling().content_ == prefix.content_);
|
|
}
|
|
}
|
|
|
|
} // namespace test
|