fix: harden arithmetic and bounds checks - #19818
Conversation
| if (adjusted >= 0) { | ||
| return currentList.get(adjusted); | ||
| final long adjusted = (long) currentSize + index; | ||
| if (adjusted >= 0 && adjusted < currentSize) { |
| if (adjusted >= 0) { | ||
| return currentList[adjusted]; | ||
| final long adjusted = (long) currentList.length + index; | ||
| if (adjusted >= 0 && adjusted < currentList.length) { |
There was a problem hiding this comment.
Pull request overview
This PR hardens arithmetic and bounds handling across Druid’s core processing, MSQ, and several extensions by widening operands before multiplication/addition, using checked arithmetic (Math.*Exact, Ints.checkedCast, Math.toIntExact), and adding explicit range validations when interpreting serialized lengths and indexes.
Changes:
- Replaced overflow-prone arithmetic with checked arithmetic and widened intermediates (e.g.,
int→long) where size/offset computations are security-sensitive. - Added explicit validations for serialized
numBytes, row ranges, enum ordinals, and buffer bounds before slicing/allocating/looping. - Added narrow CodeQL suppressions and comments where overflow/wrapping semantics are intentional (hashing, expression arithmetic, concise set cardinality).
Reviewed changes
Copilot reviewed 61 out of 61 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| server/src/test/java/org/apache/druid/segment/loading/StorageLocationTest.java | Use long loop index to match maxSize type in assertions. |
| processing/src/test/java/org/apache/druid/segment/serde/HyperUniquesSerdeForTest.java | Add explicit numBytes vs buffer-remaining validation in test serde. |
| processing/src/test/java/org/apache/druid/segment/data/V3CompressedVSizeColumnarMultiIntsSerializerTest.java | Convert numRows safely to int and iterate using the narrowed value. |
| processing/src/test/java/org/apache/druid/segment/data/SafeWritableMemoryTest.java | Widen multiplications/loop indices and avoid narrowing capacity computations. |
| processing/src/test/java/org/apache/druid/query/scan/ScanQueryRunnerFactoryTest.java | Align comparisons/loops to long scan-row limit. |
| processing/src/test/java/org/apache/druid/query/aggregation/SerializablePairLongStringBufferStoreTest.java | Remove unused narrowing accumulator. |
| processing/src/test/java/org/apache/druid/java/util/common/CompressionUtilsTest.java | Widen length * 3 expectations to long to avoid overflow. |
| processing/src/test/java/org/apache/druid/collections/bitmap/BitmapOperationAgainstConsecutiveRunsTest.java | Safely narrow minIntersection for loop bounds. |
| processing/src/main/java/org/apache/druid/timeline/SegmentId.java | Document intentional hash overflow and add targeted CodeQL suppression. |
| processing/src/main/java/org/apache/druid/segment/StringDimensionIndexer.java | Widen multiplication to long before assigning to long. |
| processing/src/main/java/org/apache/druid/segment/nested/VariantColumn.java | Use Math.addExact for dictionary id offset computations. |
| processing/src/main/java/org/apache/druid/segment/nested/NestedPathArrayElement.java | Harden negative indexing logic with widened arithmetic and bounds checks. |
| processing/src/main/java/org/apache/druid/segment/data/VSizeLongSerde.java | Avoid byte overflow by using int accumulator for bit counting. |
| processing/src/main/java/org/apache/druid/segment/data/SafeWritableMemory.java | Avoid int loop overflow by checked-casting lengthBytes once. |
| processing/src/main/java/org/apache/druid/segment/data/ImmutableRTreeObjectStrategy.java | Use checked addition when setting duplicate buffer limit. |
| processing/src/main/java/org/apache/druid/segment/data/GenericIndexed.java | Use checked addition when slicing returned ByteBuffer strategy. |
| processing/src/main/java/org/apache/druid/segment/data/FixedIndexed.java | Add width/size validation and use checked arithmetic for offsets and positioning. |
| processing/src/main/java/org/apache/druid/segment/data/CompressionStrategy.java | Use checked arithmetic when advancing positions/limits in uncompressed decompressor. |
| processing/src/main/java/org/apache/druid/segment/data/CompressedColumnarIntsSupplier.java | Validate constructor inputs and use checked subtraction. |
| processing/src/main/java/org/apache/druid/segment/data/CompressedBlockReader.java | Validate block counts/sizes and use checked arithmetic for offsets/limits. |
| processing/src/main/java/org/apache/druid/query/rowsandcols/RearrangedRowsAndColumns.java | Strengthen row-range validation and use checked subtraction for row count. |
| processing/src/main/java/org/apache/druid/query/rowsandcols/LimitedRowsAndColumns.java | Validate row range (start/end) and use checked subtraction for row count. |
| processing/src/main/java/org/apache/druid/query/rowsandcols/ArrayListRowsAndColumns.java | Validate offsets against list size and use checked subtraction for row count. |
| processing/src/main/java/org/apache/druid/query/groupby/orderby/DefaultLimitSpec.java | Use Math.addExact with overflow-to-ISE translation for limit+offset. |
| processing/src/main/java/org/apache/druid/query/cache/CacheKeyBuilder.java | Use checked addition for byte-size calculation. |
| processing/src/main/java/org/apache/druid/query/aggregation/SerializablePairLongStringComplexMetricSerde.java | Use checked addition when setting read-only buffer limit. |
| processing/src/main/java/org/apache/druid/query/aggregation/SerializablePairLongLongComplexMetricSerde.java | Use checked addition when setting read-only buffer limit. |
| processing/src/main/java/org/apache/druid/query/aggregation/SerializablePairLongFloatComplexMetricSerde.java | Use checked addition when setting read-only buffer limit. |
| processing/src/main/java/org/apache/druid/query/aggregation/SerializablePairLongDoubleComplexMetricSerde.java | Use checked addition when setting read-only buffer limit. |
| processing/src/main/java/org/apache/druid/query/aggregation/hyperloglog/HyperUniquesSerde.java | Use checked addition when setting read-only buffer limit. |
| processing/src/main/java/org/apache/druid/math/expr/BinaryMathOperatorExpr.java | Document intentional wrapping/IEEE semantics and add targeted CodeQL suppressions. |
| processing/src/main/java/org/apache/druid/java/util/http/client/io/AppendableByteArrayInputStream.java | Use checked arithmetic and checked casts when updating counters/indexes. |
| processing/src/main/java/org/apache/druid/java/util/common/io/smoosh/FileSmoosher.java | Checked-cast bytes written and use checked addition for offsets. |
| processing/src/main/java/org/apache/druid/java/util/common/granularity/PeriodGranularity.java | Use checked add/subtract when computing truncate offsets. |
| processing/src/main/java/org/apache/druid/frame/read/FrameReaderUtils.java | Use checked addition before computing region offsets for ByteBuffer slicing. |
| processing/src/main/java/org/apache/druid/frame/read/columnar/StringFrameColumnReader.java | Use checked arithmetic for string offset calculations and lengths. |
| processing/src/main/java/org/apache/druid/frame/read/columnar/ComplexFrameColumnReader.java | Use checked arithmetic for payload bounds and offset calculations. |
| processing/src/main/java/org/apache/druid/frame/key/FrameComparisonWidgetImpl.java | Narrow key-length computation to int safely and use checked allocation sizing. |
| processing/src/main/java/org/apache/druid/extendedset/intset/ConciseSet.java | Document intentional arithmetic semantics for size/cardinality paths with CodeQL suppressions. |
| processing/src/main/java/org/apache/druid/crypto/CryptoService.java | Use checked addition when computing encrypted payload buffer allocation size. |
| multi-stage-query/src/main/java/org/apache/druid/msq/statistics/QuantilesSketchKeyCollector.java | Use long loop index to avoid int overflow in weighted updates. |
| multi-stage-query/src/main/java/org/apache/druid/msq/statistics/ClusterByStatisticsCollectorImpl.java | Change retained-bytes tracking from double to long. |
| multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java | Narrow report-add counts to int and add checked conversions. |
| indexing-service/src/test/java/org/apache/druid/indexing/seekablestream/supervisor/SeekableStreamSupervisorStateManagerTest.java | Widen arithmetic in assertion to avoid overflow. |
| extensions-core/stats/src/main/java/org/apache/druid/query/aggregation/variance/VarianceSerde.java | Validate numBytes and use checked addition when setting buffer limit. |
| extensions-core/s3-extensions/src/test/java/org/apache/druid/storage/s3/S3TaskLogsTest.java | Avoid overflow-prone negation by using 1L - length expression. |
| extensions-core/s3-extensions/src/test/java/org/apache/druid/storage/s3/output/RetryableS3OutputStreamTest.java | Widen byte-count arithmetic to long in assertions. |
| extensions-core/lookups-cached-global/src/test/java/org/apache/druid/data/input/MapPopulatorTest.java | Widen string length * bytes arithmetic to long in assertion. |
| extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/FixedBucketsHistogramSerde.java | Validate numBytes and use checked addition when setting buffer limit. |
| extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/FixedBucketsHistogram.java | Replace ordinal indexing with explicit byte→enum decoding + validation. |
| extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/ApproximateHistogramFoldingSerde.java | Validate numBytes and use checked addition when setting buffer limit. |
| extensions-core/histogram/src/main/java/org/apache/druid/query/aggregation/histogram/ApproximateHistogram.java | Use long loop indices for bin repeats and avoid byte overflow on negation. |
| extensions-core/google-extensions/src/test/java/org/apache/druid/data/input/google/GoogleCloudStorageInputSourceTest.java | Widen processed-bytes expectations to long. |
| extensions-contrib/tdigestsketch/src/main/java/org/apache/druid/query/aggregation/tdigestsketch/TDigestSketchObjectStrategy.java | Validate numBytes and use checked addition for buffer limit. |
| extensions-contrib/spectator-histogram/src/test/java/org/apache/druid/spectator/histogram/SpectatorHistogramTest.java | Widen expected serialized-size arithmetic to long. |
| extensions-contrib/momentsketch/src/main/java/org/apache/druid/query/aggregation/momentsketch/MomentSketchObjectStrategy.java | Validate numBytes and use checked addition for buffer limit. |
| extensions-contrib/ddsketch/src/main/java/org/apache/druid/query/aggregation/ddsketch/DDSketchObjectStrategy.java | Validate numBytes, use checked addition, and make locals final. |
| extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/ByteBufferCompressedBigDecimal.java | Centralize index→buffer-position math with bounds checking and checked arithmetic. |
| extensions-contrib/compressed-bigdecimal/src/main/java/org/apache/druid/compressedbigdecimal/aggregator/CompressedBigDecimalAggregatorFactoryBase.java | Make locals final and use checked addition for cache-key sizing. |
| extensions-contrib/aliyun-oss-extensions/src/test/java/org/apache/druid/storage/aliyun/OssTaskLogsTest.java | Avoid overflow-prone negation by using 1L - length expression. |
| extensions-contrib/aliyun-oss-extensions/src/test/java/org/apache/druid/data/input/aliyun/OssInputSourceTest.java | Widen processed-bytes expectations to long. |
Comments suppressed due to low confidence (1)
multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java:988
- Same overflow issue as above:
Limits.MAX_WORKERS * Limits.MAX_VERBOSE_WARNINGS - workerWarnings.size()is evaluated asintbeforetoIntExact, so it can wrap. Cast tolongbefore multiplying/subtracting.
final int numReportsToAdd = Math.min(
errorReports.size(),
Math.toIntExact(Limits.MAX_WORKERS * Limits.MAX_VERBOSE_WARNINGS - workerWarnings.size())
);
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 63 out of 63 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
processing/src/main/java/org/apache/druid/segment/data/CompressedBlockReader.java:90
compressedSizeis derived from the offsets table, but it isn't validated against the remaining bytes inbufferbefore advancingbuffer.position(...). IfcompressedSizeis larger than the post-offset remaining data,compressedData.limit(...)will throw after the caller’sbufferhas already been partially consumed (advanced byoffsetsSize), which makes error handling brittle. Add an explicitcompressedSize <= buffer.remaining() - offsetsSizecheck before mutatingbuffer.position(...)so invalid serialized data fails fast without partially consuming the source buffer.
|
Addressed Copilot’s suppressed CompressedBlockReader finding in commit 2fc3590. The parser now verifies both the offsets table and declared compressed payload fit in the remaining buffer before advancing into either region. A regression test confirms an oversized payload is rejected without advancing past the offsets position. The targeted test and full processing Checkstyle/PMD/enforcer validation pass. |
FrankChen021
left a comment
There was a problem hiding this comment.
I have reviewed the code for correctness, edge cases, concurrency, and integration risks; no issues found.
Reviewed 64 of 64 changed files.
This is an automated review by Codex GPT-5.6-Sol
What changed
Why
The current CodeQL snapshot reports 108 arithmetic and bounds warnings:
java/tainted-arithmeticjava/integer-multiplication-cast-to-longjava/comparison-with-wider-typejava/implicit-cast-in-compound-assignmentjava/improper-validation-of-array-indexjava/uncontrolled-arithmeticOf these, 98 needed concrete hardening, nine intentionally preserve Java wrapping/IEEE-754/hash/cardinality semantics, and one multiplication was already widened on current upstream.
Impact
Malformed or extreme inputs now fail at explicit validation or checked-arithmetic boundaries instead of wrapping into incorrect buffer positions, allocation sizes, dictionary identifiers, or loop bounds. Normal behavior and serialized formats are unchanged.
Root cause
Many calculations were performed in a narrow type before assignment to a wider result, or relied on downstream buffer and collection APIs to reject invalid values. A few expressions intentionally use Java's defined wrapping or floating-point semantics, which static analysis cannot infer from the surrounding API contract.
Checks
git diff --checkCreated by GPT-5.6-Sol.