-
Notifications
You must be signed in to change notification settings - Fork 127
Description
Fuzzing Crash Report
Analysis
Crash Location: fuzz/fuzz_targets/array_ops.rs:18 (panicked via assert_scalar_eq at fuzz/src/array/mod.rs:750)
Error Message:
Scalar mismatch: expected decimal256(10335731459288881875449326843340756734051447447459996202947475384727173595149, precision=76, scale=75), got null in step 2
Stack Trace:
0: std::backtrace_rs::backtrace::libunwind::trace
1: std::backtrace_rs::backtrace::trace_unsynchronized
2: <std::backtrace::Backtrace>::create
3: assert_scalar_eq
at ./fuzz/src/array/mod.rs:750:13
4: run_fuzz_action
at ./fuzz/src/array/mod.rs:627:17
5: __libfuzzer_sys_run
at ./fuzz/fuzz_targets/array_ops.rs:14:11
Root Cause:
The fuzzer discovered an overflow issue in the sum operation for DecimalArray with very high precision (76). The sequence is:
- Start with a ChunkedArray containing DecimalArrays with dtype
decimal(76, 75) - Apply a mask operation (step 1) - reduces array to 9 values with some values masked out
- Apply a sum operation (step 2) - tries to sum the remaining decimal values
The sum operation detects an i256 overflow during the checked addition and returns Scalar::null instead of the actual sum value (see vortex-array/src/arrays/decimal/compute/sum.rs:64,88,104,118 - when CheckedAdd::checked_add returns None, it returns Scalar::null).
However, the expected behavior from the fuzzer's perspective is that the sum should either:
- Return the correct sum value if it fits in i256, OR
- Panic/error gracefully if overflow is detected
But currently it silently returns null, which causes a scalar mismatch when compared to the expected value computed by a reference implementation.
Array Structure:
- ChunkedArray (length 12) with dtype:
Decimal(precision=76, scale=75, Nullable) - 2 chunks: DecimalArray with 4 elements, DecimalArray with 8 elements
- Values stored as I256 type
- After mask: 9 valid values remain (mask:
[248, 15]= bits11111000 11110000reversed = 9 true bits out of 12) - Sum of these 9 large i256 values causes overflow
This is related to issue #5820 (DecimalArray logical/physical type mismatch) but represents a different failure mode - overflow handling in aggregation operations.
Debug Output
FuzzArrayAction {
array: ChunkedArray {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
len: 12,
chunk_offsets: PrimitiveArray {
dtype: Primitive(
U64,
NonNullable,
),
buffer: BufferHandle(
Host(
Buffer<u8> {
length: 24,
alignment: Alignment(
8,
),
as_slice: [0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, ...],
},
),
),
validity: NonNullable,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
chunks: [
DecimalArray {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
values: BufferHandle(
Host(
Buffer<u8> {
length: 128,
alignment: Alignment(
16,
),
as_slice: [3, 0, 0, 0, 0, 0, 0, 0, 0, 144, 236, 228, 101, 118, 200, 187, ...],
},
),
),
values_type: I256,
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
DecimalArray {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
values: BufferHandle(
Host(
Buffer<u8> {
length: 256,
alignment: Alignment(
16,
),
as_slice: [1, 0, 0, 0, 0, 0, 0, 0, 0, 208, 64, 171, 43, 14, 159, 225, ...],
},
),
),
values_type: I256,
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
],
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
actions: [
(
Mask(
Values(
MaskValues {
buffer: BitBuffer {
buffer: Buffer<u8> {
length: 2,
alignment: Alignment(
1,
),
as_slice: [248, 15],
},
offset: 0,
len: 12,
},
indices: OnceLock(
<uninit>,
),
slices: OnceLock(
<uninit>,
),
true_count: 9,
density: 0.75,
},
),
),
Array(
DecimalArray {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
values: BufferHandle(
Host(
Buffer<u8> {
length: 384,
alignment: Alignment(
16,
),
as_slice: [3, 0, 0, 0, 0, 0, 0, 0, 0, 144, 236, 228, 101, 118, 200, 187, ...],
},
),
),
values_type: I256,
validity: Array(
BoolArray {
dtype: Bool(
NonNullable,
),
bits: BufferHandle(
Host(
Buffer<u8> {
length: 2,
alignment: Alignment(
1,
),
as_slice: [7, 240],
},
),
),
offset: 0,
len: 12,
validity: NonNullable,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [
(
NullCount,
Exact(
ScalarValue(
Primitive(
U64(
0,
),
),
),
),
),
(
Min,
Exact(
ScalarValue(
Bool(
false,
),
),
),
),
(
Max,
Exact(
ScalarValue(
Bool(
true,
),
),
),
),
],
},
},
},
},
),
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
),
),
(
Sum,
Scalar(
Scalar {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
value: ScalarValue(
Decimal(
I256(
i256(
10335731459288881875449326843340756734051447447459996202947475384727173595149,
),
),
),
),
},
),
),
(
Sum,
Scalar(
Scalar {
dtype: Decimal(
DecimalDType {
precision: 76,
scale: 75,
},
Nullable,
),
value: ScalarValue(
Decimal(
I256(
i256(
10335731459288881875449326843340756734051447447459996202947475384727173595149,
),
),
),
),
},
),
),
],
}
Summary
- Target:
array_ops - Crash File:
crash-c4164c75daeb58e398ac0bac4897258859530c7e - Branch: develop
- Commit: a11336d
- Crash Artifact: https://github.com/vortex-data/vortex/actions/runs/21894513088/artifacts/5454963916
Reproduction
-
Download the crash artifact:
- Direct download: https://github.com/vortex-data/vortex/actions/runs/21894513088/artifacts/5454963916
- Or find
array_ops-crash-artifactsat: https://github.com/vortex-data/vortex/actions/runs/21894513088 - Extract the zip file
-
Reproduce locally:
# The artifact contains array_ops/crash-c4164c75daeb58e398ac0bac4897258859530c7e
cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-c4164c75daeb58e398ac0bac4897258859530c7e -- -rss_limit_mb=0- Get full backtrace:
RUST_BACKTRACE=full cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-c4164c75daeb58e398ac0bac4897258859530c7e -- -rss_limit_mb=0Auto-created by fuzzing workflow with Claude analysis