Skip to content

Commit eac19c8

Browse files
authored
fix(search): reject FT.CREATE NUMERIC BLOCKSIZE 0 (divide-by-zero) (#8164)
1 parent 4f4e7aa commit eac19c8

4 files changed

Lines changed: 38 additions & 2 deletions

File tree

src/core/search/range_tree.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,10 @@ template <typename MapT> auto FindRangeBlockImpl(MapT& entries, double value) {
7676
} // namespace
7777

7878
RangeTree::RangeTree(PMR_NS::memory_resource* mr, size_t max_range_block_size)
79-
: max_range_block_size_(max_range_block_size), entries_(mr) {
79+
// A zero block size is used as a divisor in Builder::Populate; fall back to the default.
80+
: max_range_block_size_(max_range_block_size ? max_range_block_size
81+
: kDefaultMaxRangeBlockSize),
82+
entries_(mr) {
8083
// The tree has at least always a block with a negative infinity bound, so that any new insertion
8184
// goes at least somewhere
8285
CreateEmptyBlock(-std::numeric_limits<double>::infinity());

src/core/search/range_tree_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,24 @@ TEST_F(RangeTreeTest, DiscreteIntialization) {
628628
EXPECT_EQ(result.size(), 4u);
629629
}
630630

631+
TEST_F(BuilderTest, ZeroBlockSizeDoesNotDivideByZero) {
632+
RangeTree tree{PMR_NS::get_default_resource(), 0};
633+
RangeTree::Builder builder;
634+
635+
std::vector<RangeTree::Entry> entries;
636+
entries.reserve(20);
637+
for (size_t i = 0; i < 20; i++)
638+
entries.emplace_back(i, double(i));
639+
for (auto [id, v] : entries)
640+
builder.Add(id, v);
641+
642+
builder.Populate(&tree, RenewableQuota::Unlimited());
643+
644+
rng::sort(entries, {}, &RangeTree::Entry::first);
645+
auto all_values = tree.Range(-1000, +1000);
646+
EXPECT_TRUE(rng::equal(all_values.Take(), entries | std::views::keys));
647+
}
648+
631649
// Benchmark tree insertion performance with set of discrete values
632650
static void BM_DiscreteInsertion(benchmark::State& state) {
633651
RangeTree tree{PMR_NS::get_default_resource()};

src/server/search/search_family.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,10 @@ constexpr auto kTextParamsGrammar =
204204

205205
search::SchemaField::NumericParams ParseNumericParams(CmdArgParser* parser) {
206206
search::SchemaField::NumericParams params{};
207-
parser->Check("BLOCKSIZE", &params.block_size);
207+
// A zero block size divides by zero when the numeric range tree is built.
208+
if (parser->Check("BLOCKSIZE"))
209+
params.block_size =
210+
parser->Next<facade::Positive<size_t>>("BLOCKSIZE must be a positive integer");
208211
return params;
209212
}
210213

src/server/search/search_family_test.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4074,6 +4074,18 @@ TEST_F(SearchFamilyTest, BlockSizeOptionFtCreate) {
40744074
EXPECT_THAT(resp, AreDocIds("doc:1", "doc:2", "doc:3"));
40754075
}
40764076

4077+
TEST_F(SearchFamilyTest, BlockSizeZeroRejected) {
4078+
Run({"HSET", "doc:a", "n", "7"});
4079+
4080+
EXPECT_THAT(Run({"FT.CREATE", "idx0", "ON", "HASH", "PREFIX", "1", "doc:", "SCHEMA", "n",
4081+
"NUMERIC", "BLOCKSIZE", "0"}),
4082+
ErrArg("BLOCKSIZE"));
4083+
4084+
EXPECT_THAT(Run({"FT.CREATE", "idx1", "ON", "HASH", "PREFIX", "1", "doc:", "SCHEMA", "n",
4085+
"NUMERIC", "BLOCKSIZE", "1"}),
4086+
"OK");
4087+
}
4088+
40774089
TEST_F(SearchFamilyTest, AggregateWithLoadFromJoinSimple) {
40784090
Run({"ft.create", "idx1", "ON", "HASH", "SCHEMA", "num1", "NUMERIC", "num2", "NUMERIC"});
40794091
Run({"ft.create", "idx2", "ON", "HASH", "SCHEMA", "num3", "NUMERIC", "num4", "NUMERIC"});

0 commit comments

Comments
 (0)