This is an automated email from the ASF dual-hosted git repository.
hello-stephen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new b67f4d62b78 [fix](bitmap) fix SIGSEGV when reading zero-length
serialized BITMAP/HLL/QuantileState cell (#67473)
b67f4d62b78 is described below
commit b67f4d62b781b7c4f2a12f4a39f2c1c31cc4ad50
Author: shee <[email protected]>
AuthorDate: Thu Sep 24 19:42:38 2026 +0800
[fix](bitmap) fix SIGSEGV when reading zero-length serialized
BITMAP/HLL/QuantileState cell (#67473)
When a BITMAP / HLL / QUANTILE_STATE cell on disk has a serialized
length
of 0, ColumnComplexType::insert_binary_data used to interpret the raw
page bytes as an in-memory value_type object via reinterpret_cast and
then copy-assign it, which is undefined behavior. The heap-pointer
members of BitmapValue / HyperLogLog / QuantileState were dereferenced
as if they were valid pointers, and BE processes crashed with SIGSEGV
during queries that hit read_by_rowids (e.g. lazy materialization on
aggregate / unique-key tables that carry a BITMAP value column).
Fix it by keeping the default-constructed empty object produced by
insert_default() and returning early when length == 0.
Add regression tests in be/test/core/column/column_complex_test.cpp
covering all three complex types and all three code paths that flow
into insert_binary_data.
---
be/src/core/column/column_complex.h | 3 +-
be/test/core/column/column_complex_test.cpp | 146 ++++++++++++++++++++++++++++
2 files changed, 147 insertions(+), 2 deletions(-)
diff --git a/be/src/core/column/column_complex.h
b/be/src/core/column/column_complex.h
index 9f0d7b45e72..3df7be8e63b 100644
--- a/be/src/core/column/column_complex.h
+++ b/be/src/core/column/column_complex.h
@@ -63,11 +63,10 @@ public:
void insert_binary_data(const char* pos, size_t length) {
insert_default();
- value_type* pvalue = &get_element(size() - 1);
if (!length) {
- *pvalue = *reinterpret_cast<const value_type*>(pos);
return;
}
+ value_type* pvalue = &get_element(size() - 1);
if constexpr (T == TYPE_BITMAP) {
pvalue->deserialize(pos);
diff --git a/be/test/core/column/column_complex_test.cpp
b/be/test/core/column/column_complex_test.cpp
new file mode 100644
index 00000000000..b647d3942a9
--- /dev/null
+++ b/be/test/core/column/column_complex_test.cpp
@@ -0,0 +1,146 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "core/column/column_complex.h"
+
+#include <gtest/gtest.h>
+
+#include <cstdint>
+#include <cstring>
+#include <vector>
+
+#include "core/string_ref.h"
+#include "core/value/bitmap_value.h"
+#include "core/value/hll.h"
+#include "core/value/quantile_state.h"
+
+namespace doris {
+
+static std::vector<char> make_garbage_buffer(size_t n) {
+ std::vector<char> buf(n);
+ for (size_t i = 0; i < n; ++i) {
+ buf[i] = static_cast<char>(0xAB);
+ }
+ return buf;
+}
+
+TEST(ColumnComplexTest, InsertBinaryDataZeroLengthBitmap) {
+ auto col = ColumnBitmap::create();
+ auto garbage = make_garbage_buffer(64);
+
+ col->insert_binary_data(garbage.data(), 0);
+
+ ASSERT_EQ(col->size(), 1);
+ EXPECT_EQ(col->get_element(0).get_type_code(), BitmapTypeCode::EMPTY);
+ EXPECT_EQ(col->get_element(0).cardinality(), 0);
+}
+
+TEST(ColumnComplexTest, InsertBinaryDataZeroLengthHLL) {
+ auto col = ColumnHLL::create();
+ auto garbage = make_garbage_buffer(64);
+
+ col->insert_binary_data(garbage.data(), 0);
+
+ ASSERT_EQ(col->size(), 1);
+ EXPECT_EQ(col->get_element(0).estimate_cardinality(), 0);
+}
+
+TEST(ColumnComplexTest, InsertBinaryDataZeroLengthQuantileState) {
+ auto col = ColumnQuantileState::create();
+ auto garbage = make_garbage_buffer(64);
+
+ col->insert_binary_data(garbage.data(), 0);
+
+ ASSERT_EQ(col->size(), 1);
+}
+
+TEST(ColumnComplexTest, InsertBinaryDataNonEmptyBitmap) {
+ BitmapValue bv;
+ bv.add(1);
+ bv.add(2);
+ bv.add(3);
+ size_t serialize_size = bv.getSizeInBytes();
+ std::vector<char> buf(serialize_size);
+ bv.write_to(buf.data());
+
+ auto col = ColumnBitmap::create();
+ col->insert_binary_data(buf.data(), serialize_size);
+
+ ASSERT_EQ(col->size(), 1);
+ EXPECT_EQ(col->get_element(0).cardinality(), 3);
+ EXPECT_TRUE(col->get_element(0).contains(1));
+ EXPECT_TRUE(col->get_element(0).contains(2));
+ EXPECT_TRUE(col->get_element(0).contains(3));
+}
+
+TEST(ColumnComplexTest, InsertManyContinuousBinaryDataWithZeroLengthCells) {
+ BitmapValue bv;
+ bv.add(42);
+ size_t bv_size = bv.getSizeInBytes();
+
+ std::vector<char> payload(bv_size * 2);
+ bv.write_to(payload.data());
+ bv.write_to(payload.data() + bv_size);
+
+ std::vector<uint32_t> offsets = {
+ 0,
+ 0,
+ static_cast<uint32_t>(bv_size),
+ static_cast<uint32_t>(bv_size),
+ static_cast<uint32_t>(bv_size * 2),
+ };
+
+ auto col = ColumnBitmap::create();
+ col->insert_many_continuous_binary_data(payload.data(), offsets.data(), 4);
+
+ ASSERT_EQ(col->size(), 4);
+ EXPECT_EQ(col->get_element(0).cardinality(), 0);
+ EXPECT_EQ(col->get_element(1).cardinality(), 1);
+ EXPECT_TRUE(col->get_element(1).contains(42));
+ EXPECT_EQ(col->get_element(2).cardinality(), 0);
+ EXPECT_EQ(col->get_element(3).cardinality(), 1);
+ EXPECT_TRUE(col->get_element(3).contains(42));
+}
+
+TEST(ColumnComplexTest, InsertManyStringsWithZeroLength) {
+ BitmapValue bv;
+ bv.add(7);
+ bv.add(8);
+ size_t bv_size = bv.getSizeInBytes();
+ std::vector<char> buf(bv_size);
+ bv.write_to(buf.data());
+
+ auto garbage = make_garbage_buffer(64);
+
+ std::vector<StringRef> refs = {
+ StringRef(garbage.data(), 0),
+ StringRef(buf.data(), bv_size),
+ StringRef(garbage.data(), 0),
+ };
+
+ auto col = ColumnBitmap::create();
+ col->insert_many_strings(refs.data(), refs.size());
+
+ ASSERT_EQ(col->size(), 3);
+ EXPECT_EQ(col->get_element(0).cardinality(), 0);
+ EXPECT_EQ(col->get_element(1).cardinality(), 2);
+ EXPECT_TRUE(col->get_element(1).contains(7));
+ EXPECT_TRUE(col->get_element(1).contains(8));
+ EXPECT_EQ(col->get_element(2).cardinality(), 0);
+}
+
+} // namespace doris
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]