Skip to content

fix(ffi): make FFI struct fields private to close Drop soundness hole#10431

Open
bit2swaz wants to merge 2 commits into
apache:mainfrom
bit2swaz:fix/ffi-private-fields
Open

fix(ffi): make FFI struct fields private to close Drop soundness hole#10431
bit2swaz wants to merge 2 commits into
apache:mainfrom
bit2swaz:fix/ffi-private-fields

Conversation

@bit2swaz

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #10429.

Rationale for this change

FFI_ArrowArray, FFI_ArrowSchema, and FFI_ArrowArrayStream all have public fields, including the release fn pointer that each struct's Drop impl calls. Safe Rust can construct any of them with a garbage release callback and trigger UB on drop, with no unsafe block anywhere in sight.

Drop::drop can't be unsafe fn, so there's no way to put the safety obligation on the caller at the drop site. The only fix is making the fields private, so garbage instances can't be constructed from safe code in the first place.

This came up in #10253. @Jefffrey pointed out that the Drop impl has the same problem and that FFI_ArrowArray is prone to it too, so the fix needs to cover all three structs.

What changes are included in this PR?

Stripped pub from all fields on FFI_ArrowArray (arrow-data), FFI_ArrowSchema (arrow-schema), and FFI_ArrowArrayStream (arrow-array). No new logic, no new public API.

One test in arrow-data/src/ffi.rs was accessing n_buffers and private_data directly as a child module. Replaced with ffi_array.num_buffers(), which is the existing public getter for the same information.

#[repr(C)] layout is determined by field types and order, not visibility, so the ABI is unchanged. External C producers fill these structs as raw memory and hand a pointer to Rust via from_raw, which is already unsafe.

Are these changes tested?

Yes. All existing tests pass. The soundness hole is now a compile error: constructing any of the three structs with struct literal syntax from outside the defining module fails with fields ... are private.

Are there any user-facing changes?

Breaking change for any downstream Rust code constructing these structs with struct literal syntax (e.g. FFI_ArrowArray { release: Some(f), ... }). That pattern was only valid in safe code for the struct fields that had no raw pointer invariants, but it was the exact vector for the soundness hole, so closing it is the point. Code using the public constructors (new, empty, from_raw) is unaffected.

@github-actions github-actions Bot added the arrow Changes to the arrow crate label Jul 24, 2026
@bit2swaz
bit2swaz marked this pull request as ready for review July 24, 2026 18:55
@alamb alamb added api-change Changes to the arrow API next-major-release the PR has API changes and it waiting on the next major version labels Jul 25, 2026
@alamb

alamb commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

I believe you fixed the MIRI error on #10433

Merging up to retrigger and get a clean run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api-change Changes to the arrow API arrow Changes to the arrow crate next-major-release the PR has API changes and it waiting on the next major version

Projects

None yet

Development

Successfully merging this pull request may close these issues.

soundness: FFI structs have public fields, making Drop unsound in safe code

2 participants