-
Notifications
You must be signed in to change notification settings - Fork 115
Closed
Description
Fuzzing Crash Report
Analysis
Crash Location: fuzz/src/array/mod.rs:579 (in run_fuzz_action function, Compare action)
Error Message:
Failed to compare root: vortex.alp(f32?, len=6) nbytes=15 B (100.00%) [all_valid]
metadata: ALPMetadata { exp_e: 8, exp_f: 6, patches: Some(PatchesMetadata { len: 2, offset: 0, indices_ptype: U8, chunk_offsets_len: None, chunk_offsets_ptype: None, offset_within_chunk: None }) }
encoded: vortex.constant(i32?, len=6) nbytes=5 B (33.33%) [all_valid]
metadata: EmptyMetadata
buffer (align=1): 5 B (100.00%)
patch_indices: vortex.primitive(u8, len=2) nbytes=2 B (13.33%) [sorted]
metadata: EmptyMetadata
buffer (align=1): 2 B (100.00%)
patch_values: vortex.primitive(f32?, len=2) nbytes=8 B (53.33%) [all_valid]
metadata: EmptyMetadata
buffer (align=4): 8 B (100.00%)
with >= 51015.277f32
Error: false != true at index 0, lhs is root: vortex.bool(bool?, len=6) nbytes=1 B (100.00%) [all_valid]
metadata: BoolMetadata { offset: 0 }
buffer (align=1): 1 B (100.00%)
rhs is root: vortex.bool(bool?, len=6) nbytes=1 B (100.00%) [all_valid]
metadata: BoolMetadata { offset: 0 }
buffer (align=1): 1 B (100.00%)
in step 3
Stack Trace:
0: std::backtrace_rs::backtrace::libunwind::trace
1: std::backtrace_rs::backtrace::trace_unsynchronized
2: <std::backtrace::Backtrace>::create
3: assert_array_eq
at ./fuzz/src/array/mod.rs:699:17
4: run_fuzz_action
at ./fuzz/src/array/mod.rs:578:33
Root Cause:
The fuzzer discovered a bug where comparison operations on ALP-encoded arrays with patches produce incorrect results. The issue occurs when:
- An ALP-encoded ChunkedArray with F32 Nullable dtype is created with patches
- The array undergoes compression (which uses ALP encoding)
- Two SearchSorted operations are performed successfully
- A Compare operation with
Gte(>=) operator against scalar value 51015.277 produces different results when comparing:- The compressed ALP array (with patches)
- The expected reference array (uncompressed)
The comparison returns false at index 0 for the ALP array but true for the reference array.
Looking at encodings/alp/src/alp/compute/compare.rs:35-38, the ALP compare kernel explicitly returns Ok(None) when patches are present:
if lhs.patches().is_some() {
// TODO(joe): support patches
return Ok(None);
}This forces a fallback to canonical comparison, but the fallback path appears to produce incorrect results. The bug could be in:
- How patches are applied during canonicalization before comparison
- How the fallback comparison handles the patched values
- An incorrect implementation in the compare kernel's handling of ALP arrays with patches
The array has:
- Length: 6
- Patches: 2 values at indices stored in patch_indices
- Encoded values: constant i32 array
- Patch values: f32 values
Debug Output
FuzzArrayAction {
array: ChunkedArray {
dtype: Primitive(
F32,
Nullable,
),
len: 6,
chunk_offsets: PrimitiveArray {
dtype: Primitive(
U64,
NonNullable,
),
buffer: Buffer<u8> {
length: 24,
alignment: Alignment(
8,
),
as_slice: [0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, ...],
},
validity: NonNullable,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
chunks: [
PrimitiveArray {
dtype: Primitive(
F32,
Nullable,
),
buffer: Buffer<u8> {
length: 24,
alignment: Alignment(
4,
),
as_slice: [255, 255, 71, 65, 69, 69, 69, 69, 71, 71, 71, 71, 71, 71, 71, 71, ...],
},
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
PrimitiveArray {
dtype: Primitive(
F32,
Nullable,
),
buffer: Buffer<u8> {
length: 0,
alignment: Alignment(
4,
),
as_slice: [],
},
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
],
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
actions: [
(
Compress(
Default,
),
Array(
ChunkedArray {
dtype: Primitive(
F32,
Nullable,
),
len: 6,
chunk_offsets: PrimitiveArray {
dtype: Primitive(
U64,
NonNullable,
),
buffer: Buffer<u8> {
length: 24,
alignment: Alignment(
8,
),
as_slice: [0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, ...],
},
validity: NonNullable,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
chunks: [
PrimitiveArray {
dtype: Primitive(
F32,
Nullable,
),
buffer: Buffer<u8> {
length: 24,
alignment: Alignment(
4,
),
as_slice: [255, 255, 71, 65, 69, 69, 69, 69, 71, 71, 71, 71, 71, 71, 71, 71, ...],
},
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
PrimitiveArray {
dtype: Primitive(
F32,
Nullable,
),
buffer: Buffer<u8> {
length: 0,
alignment: Alignment(
4,
),
as_slice: [],
},
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
],
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
),
),
(
SearchSorted(
Scalar {
dtype: Primitive(
F32,
Nullable,
),
value: ScalarValue(
Primitive(
F32(
51015.277,
),
),
),
},
Left,
),
Search(
Found(
2,
),
),
),
(
SearchSorted(
Scalar {
dtype: Primitive(
F32,
Nullable,
),
value: ScalarValue(
Primitive(
F32(
51015.277,
),
),
),
},
Left,
),
Search(
Found(
2,
),
),
),
(
Compare(
Scalar {
dtype: Primitive(
F32,
Nullable,
),
value: ScalarValue(
Primitive(
F32(
51015.277,
),
),
),
},
Gte,
),
Array(
BoolArray {
dtype: Bool(
Nullable,
),
bits: BitBuffer {
buffer: Buffer<u8> {
length: 1,
alignment: Alignment(
1,
),
as_slice: [60],
},
offset: 0,
len: 6,
},
validity: AllValid,
stats_set: ArrayStats {
inner: RwLock {
data: StatsSet {
values: [],
},
},
},
},
),
),
],
}
Summary
- Target:
array_ops - Crash File:
crash-7ab10e0702af30905835fe183844a0b003ece1e7 - Branch: develop
- Commit: d757be1
- Crash Artifact: https://github.com/vortex-data/vortex/actions/runs/12884951326/artifacts/2607743012
Reproduction
-
Download the crash artifact:
- Direct download: https://github.com/vortex-data/vortex/actions/runs/12884951326/artifacts/2607743012
- Or find
operations-fuzzing-crash-artifactsat: https://github.com/vortex-data/vortex/actions/runs/12884951326 - Extract the zip file
-
Reproduce locally:
# The artifact contains array_ops/crash-7ab10e0702af30905835fe183844a0b003ece1e7
cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-7ab10e0702af30905835fe183844a0b003ece1e7 -- -rss_limit_mb=0- Get full backtrace:
RUST_BACKTRACE=full cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-7ab10e0702af30905835fe183844a0b003ece1e7 -- -rss_limit_mb=0Auto-created by fuzzing workflow with Claude analysis