Skip to content

Fuzzing Crash: Integer overflow in list_view_zctl with empty sizes array #6047

@github-actions

Description

@github-actions

Fuzzing Crash Report

Analysis

Crash Location: vortex-array/src/arrow/executor/list.rs:122 in the list_view_zctl function

Error Message:

attempt to subtract with overflow

Stack Trace:

   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic_const::panic_const_sub_overflow
   3: list_view_zctl<i32>
             at ./vortex-array/src/arrow/executor/list.rs:122:20
   4: to_arrow_list<i32>
             at ./vortex-array/src/arrow/executor/list.rs:51:24
   5: execute_arrow
             at ./vortex-array/src/arrow/executor/mod.rs:124:47
   6: list_view_zctl<i32>
             at ./vortex-array/src/arrow/executor/list.rs:151:29
   7: to_arrow_list<i32>
             at ./vortex-array/src/arrow/executor/list.rs:51:24
   8: execute_arrow
             at ./vortex-array/src/arrow/executor/mod.rs:124:47
   9: into_arrow
             at ./vortex-array/src/arrow/mod.rs:45:14
  10: arrow_compare
             at ./vortex-array/src/compute/compare.rs:371:35

Root Cause:

The fuzzer discovered an integer overflow panic in the Arrow executor when converting a nested ListViewArray to an Arrow array. The crash occurs at line 122 in list_view_zctl:

let final_size = sizes
    .scalar_at(sizes.len() - 1)  // Line 122 - panics when sizes.len() == 0
    .cast(&DType::Primitive(O::PTYPE, Nullability::NonNullable))?;

When sizes.len() is 0, the expression sizes.len() - 1 causes an integer underflow panic in debug/fuzz builds.

The crash path:

  1. A nested List(List(Utf8)) structure with empty arrays is created
  2. During a compare operation, the arrays are converted to Arrow format
  3. The outer ListViewArray calls list_view_zctl
  4. The inner ListViewArray also calls list_view_zctl
  5. The inner call has an empty sizes array (length 0)
  6. Attempting sizes.len() - 1 when len() == 0 causes integer overflow

The debug output shows the structure:

  • Outer ListViewArray with 2 elements (offsets: [0, 0], sizes: [0, 0])
  • Inner ListViewArray with 0 elements (offsets: empty, sizes: empty)
  • Elements are VarBinViewArray with 0 views

This indicates a valid edge case where nested empty lists should be handled, but the current code assumes sizes.len() >= 1.

Debug Output
FuzzFileAction {
    array: ListViewArray {
        dtype: List(
            List(
                Utf8(
                    Nullable,
                ),
                Nullable,
            ),
            Nullable,
        ),
        elements: ListViewArray {
            dtype: List(
                Utf8(
                    Nullable,
                ),
                Nullable,
            ),
            elements: VarBinViewArray {
                dtype: Utf8(
                    Nullable,
                ),
                buffers: [],
                views: Buffer<vortex_vector::binaryview::view::BinaryView> {
                    length: 0,
                    alignment: Alignment(
                        16,
                    ),
                    as_slice: [],
                },
                validity: AllValid,
                stats_set: ArrayStats {
                    inner: RwLock {
                        data: StatsSet {
                            values: [],
                        },
                    },
                },
            },
            offsets: PrimitiveArray {
                dtype: Primitive(
                    U64,
                    NonNullable,
                ),
                buffer: BufferHandle(
                    Host(
                        Buffer<u8> {
                            length: 0,
                            alignment: Alignment(
                                8,
                            ),
                            as_slice: [],
                        },
                    ),
                ),
                validity: NonNullable,
                stats_set: ArrayStats {
                    inner: RwLock {
                        data: StatsSet {
                            values: [
                                (
                                    IsSorted,
                                    Exact(
                                        ScalarValue(
                                            Bool(
                                                true,
                                            ),
                                        ),
                                    ),
                                ),
                            ],
                        },
                    },
                },
            },
            sizes: PrimitiveArray {
                dtype: Primitive(
                    U64,
                    NonNullable,
                ),
                buffer: BufferHandle(
                    Host(
                        Buffer<u8> {
                            length: 0,
                            alignment: Alignment(
                                8,
                            ),
                            as_slice: [],
                        },
                    ),
                ),
                validity: NonNullable,
                stats_set: ArrayStats {
                    inner: RwLock {
                        data: StatsSet {
                            values: [],
                        },
                    },
                },
            },
            is_zero_copy_to_list: true,
            validity: AllValid,
            stats_set: ArrayStats {
                inner: RwLock {
                    data: StatsSet {
                        values: [],
                    },
                },
            },
        },
        offsets: PrimitiveArray {
            dtype: Primitive(
                I32,
                NonNullable,
            ),
            buffer: BufferHandle(
                Host(
                    Buffer<u8> {
                        length: 8,
                        alignment: Alignment(
                            4,
                        ),
                        as_slice: [0, 0, 0, 0, 0, 0, 0, 0],
                    },
                ),
            ),
            validity: NonNullable,
            stats_set: ArrayStats {
                inner: RwLock {
                    data: StatsSet {
                        values: [
                            (
                                IsSorted,
                                Exact(
                                    ScalarValue(
                                        Bool(
                                            true,
                                        ),
                                    ),
                                ),
                            ),
                        ],
                    },
                },
            },
        },
        sizes: PrimitiveArray {
            dtype: Primitive(
                I32,
                NonNullable,
            ),
            buffer: BufferHandle(
                Host(
                    Buffer<u8> {
                        length: 8,
                        alignment: Alignment(
                            4,
                        ),
                        as_slice: [0, 0, 0, 0, 0, 0, 0, 0],
                    },
                ),
            ),
            validity: NonNullable,
            stats_set: ArrayStats {
                inner: RwLock {
                    data: StatsSet {
                        values: [],
                    },
                },
            },
        },
        is_zero_copy_to_list: true,
        validity: AllValid,
        stats_set: ArrayStats {
            inner: RwLock {
                data: StatsSet {
                    values: [],
                },
            },
        },
    },
    projection_expr: None,
    filter_expr: None,
    compressor_strategy: Compact,
}

Summary

  • Target: file_io
  • Crash File: crash-f59d24f0d4e13cdb621ff55a229aa3153a9eaeb0
  • Branch: $BRANCH
  • Commit: $COMMIT
  • Crash Artifact: $ARTIFACT_URL

Reproduction

  1. Download the crash artifact:

    • Direct download: $ARTIFACT_URL
    • Or find $ARTIFACT_NAME at: $WORKFLOW_RUN
    • Extract the zip file
  2. Reproduce locally:

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

Auto-created by fuzzing workflow with Claude analysis

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions