cetra3 opened a new issue, #10360:
URL: https://github.com/apache/arrow-rs/issues/10360

   # `PartialEq` on unvalidated deeply-nested variants overflows the stack 
(SIGABRT)
   
   ## Describe the bug
   
   `VariantList::eq` (`list.rs:313`) and `VariantObject::eq` are hand-written 
and recurse into
   child values with no depth bound. Comparing two deeply-nested variants that 
were constructed
   with the shallow `Variant::new` constructor overflows the stack and **aborts 
the process**
   (`SIGABRT`). Unlike a panic, a stack-overflow abort cannot be contained by 
`catch_unwind`.
   
   This is *not* closed by the validation depth limit added in #10352: that 
limit
   (`MAX_NESTING_DEPTH`) is enforced inside `with_full_validation`, and 
`Variant::new` never calls
   it. So `eq` on a `Variant::new` value has no gate.
   
   ## To Reproduce
   
   ```rust
   use parquet_variant::Variant;
   
   /// `depth` single-element lists nested around a null primitive.
   fn make_nested_lists(depth: usize) -> Vec<u8> {
       let mut value = vec![0u8]; // null primitive
       for _ in 0..depth {
           let mut outer = vec![0x0F, 1]; // array header, 4-byte offsets, 
num_elements=1
           outer.extend_from_slice(&0u32.to_le_bytes());
           outer.extend_from_slice(&(value.len() as u32).to_le_bytes());
           outer.append(&mut value);
           value = outer;
       }
       value
   }
   
   let metadata = [0x01u8, 0, 0]; // empty dictionary
   let value = make_nested_lists(50_000);
   
   // Variant::new does only shallow validation -- it never calls 
with_full_validation,
   // so the depth limit that guards try_new is never on this path.
   let a = Variant::new(&metadata, &value);
   let b = Variant::new(&metadata, &value);
   
   let _ = a == b; // recurses once per level with no bound -> stack overflow
   ```
   
   Output:
   
   ```
   thread '...' has overflowed its stack
   fatal runtime error: stack overflow, aborting
   (signal: 6, SIGABRT: process abort signal)
   ```
   
   (Depth 50,000 is well past the edge; on a 2 MiB stack it aborts in the low 
thousands of levels.)
   
   ## Root cause
   
   The depth counter that bounds recursion lives in `with_full_validation`. 
`Variant::new`
   performs only constant-time shallow validation and never calls it, so 
nothing gates the
   recursion in the hand-written `eq` implementations for lists and objects.
   
   ## Expected behavior
   
   Comparing two variants should never abort the process, even for unvalidated 
inputs. Recursion
   should be bounded inside `eq` itself — e.g. a depth guard, or an iterative 
comparison — so that
   `==` on a `Variant::new` value fails gracefully (or simply returns) rather 
than overflowing the
   stack.
   
   ## Additional context
   
   Reachability is narrower than a typical parsing bug: it requires opting out 
of validation via
   `Variant::new` and then comparing. Validated input (via `try_new`) is capped 
at
   `MAX_NESTING_DEPTH` by #10352, so this cannot be reached through the 
validating path.
   
   One thing worth calling out regardless of how the fix is prioritised: the 
type documentation
   warns that infallible access on an *unvalidated* instance "may panic". That 
guarantee is weaker
   than it reads here — a stack-overflow abort is **not** a panic, so a caller 
who wrapped their
   comparison in `catch_unwind` on the strength of that doc would still lose 
the whole process.
   
   `eq` is the concrete instance found; more broadly, any hand-written 
recursive traversal
   reachable from a `Variant::new` value is exposed to the same 
unbounded-recursion shape. (Note
   `Debug` is `#[derive]`d over the byte buffers and does not recurse into 
decoded children, so it
   is not affected.)
   
   Found while adding a `proptest` fuzzing harness to `parquet-variant` in 
#10352; this is a
   follow-up to that work and, as noted above, is deliberately *not* addressed 
by that PR's depth
   limit. Investigation was assisted by an agent-driven proptest harness.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to