Skip to content

fix(arrow-buffer): add a fallible collect_bool - #10984

Open
Cintu07 wants to merge 5 commits into
apache:mainfrom
Cintu07:fix/collect-bool-fallible-reservation
Open

fix(arrow-buffer): add a fallible collect_bool#10984
Cintu07 wants to merge 5 commits into
apache:mainfrom
Cintu07:fix/collect-bool-fallible-reservation

Conversation

@Cintu07

@Cintu07 Cintu07 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Rationale for this change

collect_bool reserves ceil(len / 64) u64 words before it calls the closure once, so a len that comes from a row or bit count in user controlled data aborts the process rather than failing. the reservation went through Vec::with_capacity, which has no way to report that.

this file already has the shape for it. with_capacity is try_with_capacity(..).unwrap_or_else(|e| panic!("{e}")) at line 154, and there are seven other fallible twins beside it: try_from_len_zeroed, try_reserve, try_repeat_slice_n_times, try_resize, try_shrink_to_fit, try_extend_from_slice and try_extend_zeros. collect_bool is the allocating entry point that does not have one.

What changes are included in this PR?

  • add try_collect_bool returning Result<Self, MutableBufferError>
  • collect_bool calls it and unwraps, the same way with_capacity calls try_with_capacity
  • the reservation goes through Vec::try_reserve, and a failure maps onto the existing AllocationError variant carrying the layout it tried to take

no behaviour change for any len that already worked. collect_bool still panics, one level down, and its doc comment now says so.

i went with a fallible entry point rather than capping the reservation, since a cap only moves where the abort happens rather than letting a caller handle it. happy to do the cap instead if you would rather not grow the api surface.

Are these changes tested?

yes, two tests.

try_collect_bool_reports_a_len_it_cannot_reserve uses the 2^60 from the issue and asserts it comes back as AllocationError and that the closure ran zero times. the reproducer in the issue aborts on that same input with memory allocation of 144115188075855872 bytes failed.

try_collect_bool_matches_collect_bool_for_sizes_that_fit checks the two agree byte for byte at 0, 1, 63, 64, 65 and 1000 bits, since 64 is the word boundary and 8 is the truncation boundary.

arrow-buffer is 351 passed, arrow-array is 721 passed, and fmt and clippy with -D warnings are clean. i ran arrow-array as well because collect_bool has around twenty call sites in it and this changes how the function is built.

Are there any user-facing changes?

try_collect_bool is new and additive. collect_bool keeps its signature and still panics on a len it cannot reserve.

collect_bool reserves ceil(len / 64) u64 words before calling the
closure, so a len taken from a row or bit count in user controlled data
aborts the process rather than failing. The reservation used
Vec::with_capacity, which has no way to report that.

Add try_collect_bool returning Result<Self, MutableBufferError> and
implement collect_bool on top of it, matching the with_capacity and
try_with_capacity pairing already in this file. The reservation now goes
through Vec::try_reserve and maps a failure onto the existing
AllocationError variant.
Copilot AI lite review requested due to automatic review settings September 4, 2026 11:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added arrow Changes to the arrow crate arrow-buffer labels Sep 4, 2026
Comment thread arrow-buffer/src/buffer/mutable.rs Outdated
buf.reserve(usize::MAX);
}
#[test]
fn try_collect_bool_reports_a_len_it_cannot_reserve() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Cintu07 this is causing the miri to fail

test buffer::mutable::tests::try_collect_bool_reports_a_len_it_cannot_reserve ... 
  stderr ───
    warning: extern crate `criterion` is unused in crate `arrow_buffer`
      --> arrow-buffer/src/lib.rs:18:1
       |
    18 | //! Low-level buffer abstractions for [Apache Arrow Rust](https://docs.rs/arrow)
       | ^
       |
       = help: remove the dependency or add `use criterion as _;` to the crate root
       = note: requested on the command line with `--force-warn unused-crate-dependencies`

    error: resource exhaustion: tried to allocate more memory than available to compiler
       --> /home/runner/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:568:13
        |
    568 |             self.alloc.allocate(new_layout)
        |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ resource exhaustion occurred here
        |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, fixed in a2e0417. #[cfg_attr(miri, ignore)] on that one the way the rest of
the repo does it. the other test only uses small sizes so it should still run under
miri, ci will confirm.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to not skip the miri check here.

Im not sure that we need either of these test 🤔. I think the fix here is self explanatory enough to not warrant them.

Miri interprets the allocation rather than handing it to the system
allocator, so reserving 2^60 bits exhausts the interpreter itself
instead of coming back as an error. Ignore that test under miri, the
way the other oversized and inline-assembly tests in the repo already
are. The small size test keeps running there, so the new code path is
still covered under miri.
Comment thread arrow-buffer/src/buffer/mutable.rs Outdated
buf.reserve(usize::MAX);
}
#[test]
fn try_collect_bool_reports_a_len_it_cannot_reserve() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to not skip the miri check here.

Im not sure that we need either of these test 🤔. I think the fix here is self explanatory enough to not warrant them.

Comment thread arrow-buffer/src/buffer/mutable.rs Outdated
}

#[test]
fn try_collect_bool_matches_collect_bool_for_sizes_that_fit() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I don't think try_collect_bool does enough thats different from collect_bool to warrant adding another test

@Cintu07 Cintu07 Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dropped this one too in b58729e.

Comment thread arrow-buffer/src/buffer/mutable.rs Outdated

/// Fallible version of [`MutableBuffer::collect_bool`].
///
/// `len` is a bit count, so the reservation is `ceil(len / 64)` words. Reserving that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This is true if “words” means u64 storage words, but it’s a bit ambiguous on 32-bit targets where “word” usually means 32 bits. Maybe say ceil(len / 64) u64 slots instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in b58729e, reads ceil(len / 64) u64 slots now.

Per review: the two tests are removed rather than skipped under miri, and
the reservation doc says u64 slots instead of words, which was ambiguous
on 32 bit targets.

@Rich-T-kid Rich-T-kid left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, looks good to me. thank you @Cintu07

Comment thread arrow-buffer/src/buffer/mutable.rs Outdated
Comment thread arrow-buffer/src/buffer/mutable.rs Outdated
@Jefffrey Jefffrey added the bug label Sep 5, 2026
Cintu07 and others added 2 commits September 5, 2026 09:03
Co-authored-by: Jeffrey Vo <jeffrey.vo.australia@gmail.com>
Co-authored-by: Jeffrey Vo <jeffrey.vo.australia@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arrow Changes to the arrow crate arrow-buffer bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MutableBuffer::collect_bool allocates from len before any iteration

4 participants