Skip to content

Fuzzing Crash: DecimalArray fill_null with null fill value #5806

Description

@github-actions

Fuzzing Crash Report

Analysis

Crash Location: vortex-array/src/arrays/decimal/compute/fill_null.rs:35:fill_null

Error Message:

top-level fill_null ensure non-null fill value

Stack Trace:

   6: fill_null
             at ./vortex-array/src/arrays/decimal/compute/fill_null.rs:35:26
   7: invoke<vortex_array::arrays::decimal::vtable::DecimalVTable>
             at ./vortex-array/src/compute/fill_null.rs:84:13
   8: invoke
             at ./vortex-array/src/compute/fill_null.rs:114:42
   9: invoke
             at ./vortex-array/src/compute/mod.rs:149:34
  10: fill_null
             at ./vortex-array/src/compute/fill_null.rs:54:10
  11: run_fuzz_action
             at ./fuzz/src/array/mod.rs:594:33

Root Cause: The fill_null operation is being called on a DecimalArray with a NULL fill value. The code at line 35 attempts to extract and cast the decimal value from the fill scalar:

let fill_value = fill_value
    .as_decimal()
    .decimal_value()
    .and_then(|v| v.cast::<T>())
    .vortex_expect("top-level fill_null ensure non-null fill value");

However, when the fill_value scalar contains a null value (ScalarValue(Null)), the decimal_value() call returns None, causing the panic. The fill_null operation semantically doesn't make sense with a null fill value - you can't fill nulls with null - but the code should either validate this earlier or handle it gracefully.

Debug Output
FuzzArrayAction {
    array: DecimalArray {
        dtype: Decimal(
            DecimalDType {
                precision: 39,
                scale: -53,
            },
            Nullable,
        ),
        values: Buffer<u8> {
            length: 96,
            alignment: Alignment(
                16,
            ),
            as_slice: [44, 1, 52, 255, 127, 159, 156, 85, 141, 146, 73, 163, 37, 123, 121, 84, ...],
        },
        values_type: I256,
        validity: AllValid,
        stats_set: ArrayStats {
            inner: RwLock {
                data: StatsSet {
                    values: [],
                },
            },
        },
    },
    actions: [
        (
            Take(
                SparseArray {
                    ...
                    fill_value: Scalar {
                        dtype: Primitive(
                            U64,
                            Nullable,
                        ),
                        value: ScalarValue(
                            Null,
                        ),
                    ...

The key issue is visible in the debug output where the fill_value is ScalarValue(Null).

Summary

Reproduction

  1. Download the crash artifact:

  2. Reproduce locally:

# The artifact contains array_ops/crash-f8bcb31cf04f44da830e3be55a3c71f82534ddc2
cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-f8bcb31cf04f44da830e3be55a3c71f82534ddc2 -- -rss_limit_mb=0
  1. Get full backtrace:
RUST_BACKTRACE=full cargo +nightly fuzz run -D --sanitizer=none array_ops array_ops/crash-f8bcb31cf04f44da830e3be55a3c71f82534ddc2 -- -rss_limit_mb=0

Auto-created by fuzzing workflow with Claude analysis

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugA bug issuefuzzerIssues detected by the fuzzer

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions