Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/iceberg/deletes/roaring_position_bitmap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,17 @@ Result<RoaringPositionBitmap> RoaringPositionBitmap::Deserialize(std::string_vie
} catch (const std::exception& e) {
return InvalidArgument("Failed to deserialize bitmap at key {}: {}", key, e.what());
}

// readSafe does not verify structural invariants; an invalid bitmap can cause
// out-of-bounds access in later merge/serialize, so validate here.
const char* invalid_reason = nullptr;
if (!roaring::api::roaring_bitmap_internal_validate(&bitmap.roaring,
&invalid_reason)) {
return InvalidArgument(
"Invalid bitmap at key {}: {}", key,
invalid_reason != nullptr ? invalid_reason : "unknown reason");
}

size_t bitmap_size = bitmap.getSizeInBytes(/*portable=*/true);
ICEBERG_PRECHECK(
bitmap_size <= remaining,
Expand Down
25 changes: 25 additions & 0 deletions src/iceberg/test/roaring_position_bitmap_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,31 @@ TEST(RoaringPositionBitmapTest, TestSerializeDeserializeAllContainerBitmap) {
AssertEqualContent(copy, expected_positions);
}

TEST(RoaringPositionBitmapTest, TestDeserializeRejectsStructurallyInvalidBitmap) {
auto append_le = [](std::string& out, uint64_t value, int bytes) {
for (int i = 0; i < bytes; ++i) {
out.push_back(static_cast<char>((value >> (8 * i)) & 0xFF));
}
};

std::string blob;
append_le(blob, 1, 8); // one bitmap
append_le(blob, 0, 4); // key 0

// Portable roaring without run containers: one bitset container whose header
// claims cardinality 4097 while all 65536 bits are actually set.
append_le(blob, 12346, 4); // SERIAL_COOKIE_NO_RUNCONTAINER
append_le(blob, 1, 4); // container count
append_le(blob, 0, 2); // container key
append_le(blob, 4096, 2); // cardinality - 1
append_le(blob, 8 + 4 + 4 + 4 + 4, 4); // data offset (unused by readSafe)
blob.append(8192, '\xFF');

auto result = RoaringPositionBitmap::Deserialize(blob);
ASSERT_THAT(result, IsError(ErrorKind::kInvalidArgument));
ASSERT_THAT(result, HasErrorMessage("Invalid bitmap at key 0"));
}

TEST(RoaringPositionBitmapTest, TestForEach) {
RoaringPositionBitmap bitmap;
bitmap.Add(30L);
Expand Down
Loading