github-actions[bot] commented on code in PR #66877:
URL: https://github.com/apache/doris/pull/66877#discussion_r3842539393


##########
be/src/storage/index/snii/snii_index_reader.cpp:
##########
@@ -1100,11 +1126,35 @@ Status SniiIndexReader::_try_count_only_fastpath(
         RETURN_IF_ERROR(
                 ::doris::snii::query::count_only_term_df(*logical_reader, 
physical_term, &count));
     }
+    // df bounds the fabricated bitmap, so it has to be inside a document 
domain
+    // that is itself real. Two steps, because they fail differently.
+    const auto& stats = logical_reader->stats();
+    if (count > stats.doc_count || count > stats.indexed_doc_count) {

Review Comment:
   [P1] Prove the posting, not only df bounds
   
   These checks only prove that `DictEntry::df` is numerically in range; they 
do not prove the row-accurate posting result. The existing fixture can keep 
posting `{2,7}`/encoded `n=2`, set `df=1`, and rebuild the DICT CRC, so honest 
Core/segment counts of 10 still publish 1 instead of 2. Conversely, with 
`df=n=2` and the same honest domains, a CRC-valid posting `{2,12}` publishes 2 
here while the normal segment intersection leaves one row. Please either 
validate both `df == payload cardinality` and every posting member against the 
real segment domain, or gate this shortcut on a format capability that 
independently guarantees both invariants, with regressions for both shapes.



##########
be/src/storage/index/snii/snii_index_reader.cpp:
##########
@@ -1100,11 +1126,35 @@ Status SniiIndexReader::_try_count_only_fastpath(
         RETURN_IF_ERROR(
                 ::doris::snii::query::count_only_term_df(*logical_reader, 
physical_term, &count));
     }
+    // df bounds the fabricated bitmap, so it has to be inside a document 
domain
+    // that is itself real. Two steps, because they fail differently.
+    const auto& stats = logical_reader->stats();
+    if (count > stats.doc_count || count > stats.indexed_doc_count) {
+        return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
+                "SNII count fast path: term df {} exceeds document domain (doc 
count {}, "
+                "indexed doc count {})",
+                count, stats.doc_count, stats.indexed_doc_count);
+    }
+    // Both limits above are CRC-valid fields of the SAME image as df, so an 
image
+    // whose stats were inflated together with df clears them: on a real 10-row
+    // segment, df = doc_count = indexed_doc_count = 100 fabricates 100 ids, 
and
+    // SegmentIterator -- which seeds _row_bitmap with [0, num_rows) and 
intersects
+    // -- silently reports 10. The segment's own row count is the one bound the
+    // image cannot move. One-sided on purpose: an index covering FEWER rows 
than
+    // the segment still fabricates ids inside [0, num_rows), so only an 
oversized
+    // domain is corruption. This mirrors the equality SniiSegmentReader::
+    // load_inherited_index already demands of a rewrite.
+    if (stats.doc_count > _rows_of_segment) {
+        return Status::Error<ErrorCode::INVERTED_INDEX_FILE_CORRUPTED, false>(
+                "SNII count fast path: index doc count {} exceeds the segment 
row count {}",
+                stats.doc_count, _rows_of_segment);
+    }
 
-    // Null handling. df is the exact match count REGARDLESS of nulls: the
-    // writer adds no tokens for a null doc (scalar add_nulls; a NULL array row
-    // is an empty range), so postings -- and therefore df -- never include
-    // null rows, exactly matching MATCH's "null never matches" semantics. The
+    // Null handling. df is the exact match count REGARDLESS of nulls: the 
writer

Review Comment:
   [P1] Prove null-frame and posting consistency
   
   These bounds validate only Core, but the nullable branch consumes a 
separately CRC-valid frame through `_read_null_bitmap()`, skipping the 
document-count, cardinality, and docid checks in 
`LogicalIndexReader::read_null_docids()`. With segment/Core 10 and Core 
`{indexed=8,null=2}`, a frame `{0..8}` makes accurate `df=2` fabricate 
`{9,10}`; the segment clamp returns 1 while posting `{2,7}` masked by that 
frame returns 0. Even a fully correlated frame `{1,3}` is insufficient if a 
CRC-valid scalar posting `{1,7}` has `df=n=2`: fabrication relocates to `{0,2}` 
and returns 2, while normal null masking returns 1. Please validate the exact 
loaded/cached frame against Core and require scalar posting/null disjointness 
(or an independent format capability) before publishing, return corruption on 
mismatch, and cover both cases.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to