Skip to content

Fuzzing Crash: array_ops - BtrBlocksCompressor loses list data with empty strings in struct fields #5394

Description

@github-actions

Fuzzing Crash Report

Analysis

Crash Location: fuzz/fuzz_targets/array_ops.rs:37:71 (unwrap on assertion failure in assert_array_eq)

Error Message:

called `Result::unwrap()` on an `Err` value: {==(: decimal128(1956805334888731279340369011, precision=28, scale=-36), : ["", "", "", "", "", ""]} != {==(: decimal128(1956805334888731279340369011, precision=28, scale=-36), : []} at index 11, lhs is root: vortex.struct({==(=decimal(28,-36)?, =list(utf8?)?}?, len=27) nbytes=1.13 kB

Stack Trace:

   0: __rustc::rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::result::unwrap_failed
   3: unwrap<(), vortex_fuzz::error::VortexFuzzError>
   4: __libfuzzer_sys_run at ./fuzz/fuzz_targets/array_ops.rs:37:71
   5: rust_fuzzer_test_input
   6: {closure#0}
   7: do_call<libfuzzer_sys::test_input_wrap::{closure_env#0}, i32>

Root Cause:

The fuzzer detected data loss during compression (step 2) using the Default compressor (BtrBlocksCompressor). The test performed:

  1. Filter operation (AllTrue mask) - succeeded
  2. Filter operation (partial mask, 27/28 elements) - succeeded
  3. Compress with BtrBlocksCompressor - FAILED

After compression, the array values at index 11 differ:

  • Expected (lhs): Struct with list field containing ["", "", "", "", "", ""] (6 empty strings)
  • Actual (rhs): Struct with list field containing [] (empty list)

The input structure:

  • ChunkedArray of Structs (len: 28, 3 chunks)
  • Each struct has 2 fields:
    • Field "==(\u{4}": Decimal(precision=28, scale=-36, nullable)
    • Field "" (empty name): List<Utf8(nullable), nullable>
  • The List field uses ListViewArray encoding with empty VarBinViewArray elements
  • At index 11, the ListViewArray has validity but all list sizes are 0 (6 empty lists)

The compressor appears to incorrectly deduplicate or compress empty list structures within struct fields, causing list data to be lost when converting from ListViewArray with 6 valid-but-empty sublists to an empty list.

Debug Output
FuzzArrayAction {
    array: ChunkedArray {
        dtype: Struct(
            StructFields {
                names: FieldNames([
                    FieldName("==(\u{4}"),
                    FieldName(""),
                ]),
                dtypes: [
                    FieldDType { inner: Owned(Decimal(DecimalDType { precision: 28, scale: -36 }, Nullable)) },
                    FieldDType { inner: Owned(List(Utf8(Nullable), Nullable)) },
                ],
            },
            Nullable,
        ),
        len: 28,
        chunk_offsets: [0, 6, 25, 28],
        chunks: [
            StructArray { len: 6, fields: [Decimal chunks, ListViewArray chunks with empty elements] },
            StructArray { len: 19, fields: [Decimal, ListViewArray with 15 VarBinViewArray elements] },
            StructArray { len: 3, fields: [Decimal, ListViewArray with empty elements] },
        ],
    },
    actions: [
        (Filter(AllTrue(28)), ...), // Step 0: success
        (Filter(Values(27 true)), ...), // Step 1: success
        (Compress(Default), ...), // Step 2: FAILURE - array value mismatch at index 11
    ],
}

The ListViewArray at index 11 (in the second chunk):

  • Contains 6 list entries with all sizes = 0 (empty strings)
  • Uses validity array with some entries valid
  • After compression: becomes empty list instead of list with 6 empty elements

Summary

Reproduction

  1. Download the crash artifact:

  2. Reproduce locally:

# The artifact contains array_ops/crash-2a20d373d9e96fb70cc7336d457d211385d447e7
cargo +nightly fuzz run --sanitizer=none array_ops array_ops/crash-2a20d373d9e96fb70cc7336d457d211385d447e7
  1. Get full backtrace:
RUST_BACKTRACE=full cargo +nightly fuzz run --sanitizer=none array_ops array_ops/crash-2a20d373d9e96fb70cc7336d457d211385d447e7

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