Fix signed integer overflow in toStartOfInterval for Millisecond/Microsecond intervals#100156
Conversation
…crosecond intervals The multiplication `milliseconds * scale_diff` (and similarly for microseconds) in `ToStartOfInterval` could overflow Int64 when the interval value is very large and the DateTime64 scale requires rescaling. Use `mulOverflow` to detect this and throw `DECIMAL_OVERFLOW` instead of triggering undefined behavior. Found by AST fuzzer (UBSan): https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=100138&sha=f2962399a5dcac395a5540de8d06c3500e263ada&name_0=PR&name_1=AST%20fuzzer%20%28amd_ubsan%29 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…visors The addition `res += static_cast<OriginResultType>(b)` in `PositiveModuloImpl` can overflow signed arithmetic when B is unsigned and b > INT64_MAX, because the cast to signed produces a negative value. Perform the addition in unsigned arithmetic instead, which is well-defined and produces the correct result since the mathematical value is always in [0, |b|). Caught by `FunctionsStress.stress` unit test under UBSan. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a stateless test that directly exercises the `positiveModulo` overflow edge cases with large unsigned divisors, as requested in the code review. The test covers the specific scenario where the intermediate addition `res + b` in `PositiveModuloImpl` would overflow signed Int64 arithmetic. #100156 (comment) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Interval-overflow
…Interval-overflow # Conflicts: # src/Functions/DivisionUtils.h
…Interval-overflow
| throw DB::Exception(ErrorCodes::DECIMAL_OVERFLOW, "Numeric overflow"); | ||
| if (t >= 0) [[likely]] /// When we divide the `t` value we should round the result | ||
| return (t + scale_diff / 2) / (microseconds * scale_diff) * microseconds; | ||
| return (t + scale_diff / 2) / microseconds_scaled * microseconds; |
There was a problem hiding this comment.
mulOverflow covers microseconds * scale_diff, but the subsequent rounding expression still has signed-overflow UB for large positive DateTime64 values:
(t + scale_diff / 2) / microseconds_scaled * microsecondsConcrete trace for scale_multiplier = 10^9 (so scale_diff = 1000 in the microsecond branch) and
t = toDateTime64('2262-04-11 23:47:16.854775807', 9) (t == 9223372036854775807):
scale_diff / 2 == 500t + 500overflowsInt64before the division
So this PR still leaves UB reachable in the same code path it is fixing. Please guard the addition with common::addOverflow (or equivalent) and throw DECIMAL_OVERFLOW on overflow.
The same pattern is also present in the millisecond specialization.
| throw DB::Exception(ErrorCodes::DECIMAL_OVERFLOW, "Numeric overflow"); | ||
| if (t >= 0) [[likely]] /// When we divide the `t` value we should round the result | ||
| return (t + scale_diff / 2) / (milliseconds * scale_diff) * milliseconds; | ||
| return (t + scale_diff / 2) / milliseconds_scaled * milliseconds; |
There was a problem hiding this comment.
Same overflow pattern exists in the millisecond specialization:
(t + scale_diff / 2) / milliseconds_scaled * millisecondsFor large positive t close to INT64_MAX and scale_multiplier > 1000, t + scale_diff / 2 can overflow before division. Please apply the same common::addOverflow guard here and throw DECIMAL_OVERFLOW on overflow.
…Interval-overflow # Conflicts: # src/Functions/DateTimeTransforms.h
Use already-computed `microseconds_scaled` / `milliseconds_scaled` instead of recomputing the same product into a `divisor` variable inside the block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Interval-overflow
Use a negative `DateTime64` (pre-1970) in the `toStartOfInterval` overflow test so it exercises the `else` branch where master lacked the `mulOverflow` check. The previous test used a positive timestamp which hit the `t >= 0` branch where the overflow check already existed on master, causing bugfix validation to fail (test passed on both old and new code). Remove the `positiveModulo` overflow test since the underlying fix was already merged to master via a separate PR, making this test irrelevant for bugfix validation of this PR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…Interval-overflow
…Interval-overflow
|
The Stress test (arm_msan) failure is fixed by #101239, which should be merged first. After it is merged, please update the branch to include the fix. |
LLVM Coverage Report
Changed lines: 100.00% (30/30) | lost baseline coverage: 32 line(s) · Uncovered code |

The multiplication
interval * scale_diffinToStartOfInterval<Millisecond>andToStartOfInterval<Microsecond>could overflowInt64when the interval value is very large and theDateTime64scale requires rescaling. UsemulOverflowto detect this and throwDECIMAL_OVERFLOWinstead of triggering undefined behavior.Found by AST fuzzer (UBSan): https://s3.amazonaws.com/clickhouse-test-reports/json.html?PR=100138&sha=f2962399a5dcac395a5540de8d06c3500e263ada&name_0=PR&name_1=AST%20fuzzer%20%28amd_ubsan%29
#100138
Changelog category (leave one):
Changelog entry (a user-readable short description of the changes that goes into CHANGELOG.md):
Fix signed integer overflow (undefined behavior) in
toStartOfIntervalwhen called with very large millisecond/microsecond interval values on high-precisionDateTime64.Documentation entry for user-facing changes
Version info
26.4.1.737