-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(ffi): make FFI struct fields private to close Drop soundness hole #10431
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,31 +37,41 @@ use std::ffi::c_void; | |
| #[repr(C)] | ||
| #[derive(Debug)] | ||
| pub struct FFI_ArrowArray { | ||
| // Fields are private so safe code can't set them to values that break import | ||
| // or the release callback (e.g. a bad `buffers` pointer gets dereferenced on | ||
| // import). Read them with the getters; write the callback fields with the | ||
| // unsafe setters. | ||
| /// Logical length of the array | ||
| pub length: i64, | ||
| length: i64, | ||
| /// Number of null items in the array | ||
| pub null_count: i64, | ||
| null_count: i64, | ||
| /// logical offset inside the array | ||
| pub offset: i64, | ||
| offset: i64, | ||
| /// Number of physical buffers backing this array | ||
| pub n_buffers: i64, | ||
| n_buffers: i64, | ||
| /// Number of children this array has | ||
| pub n_children: i64, | ||
| n_children: i64, | ||
| /// C array of pointers to the start of each physical buffer backing this array | ||
| pub buffers: *mut *const c_void, | ||
| buffers: *mut *const c_void, | ||
| /// C array of pointers to each child array of this array | ||
| pub children: *mut *mut FFI_ArrowArray, | ||
| children: *mut *mut FFI_ArrowArray, | ||
| /// Pointer to the underlying array of dictionary values | ||
| pub dictionary: *mut FFI_ArrowArray, | ||
| /// Pointer to a producer-provided release callback | ||
| pub release: Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowArray)>, | ||
| dictionary: *mut FFI_ArrowArray, | ||
| /// Producer-provided release callback. | ||
| /// | ||
| /// Private so safe code can't install a callback that [`Drop`] would invoke. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the goal is to avoid potentially unsafe access do we have to change the other fields to so they aren't pub? For example if I set the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. kept just the per-field its additive and non-breaking too so easy to add later if a real from-scratch case shows up. didnt wanna ship unsafe api with no caller yet so yeah |
||
| /// Use [`FFI_ArrowArray::release`] and [`FFI_ArrowArray::set_release`]. | ||
| release: Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowArray)>, | ||
| /// Opaque pointer to producer-provided private data | ||
| /// When exported, this MUST contain everything that is owned by this array. | ||
| /// For example, any buffer pointed to in `buffers` must be here, as well | ||
| /// as the `buffers` pointer itself. | ||
| /// In other words, everything in [FFI_ArrowArray] must be owned by | ||
| /// `private_data` and can assume that they do not outlive `private_data`. | ||
| pub private_data: *mut c_void, | ||
| /// | ||
| /// Private for the same reason as `release`. Use | ||
| /// [`FFI_ArrowArray::private_data`] and [`FFI_ArrowArray::set_private_data`]. | ||
| private_data: *mut c_void, | ||
| } | ||
|
|
||
| impl Drop for FFI_ArrowArray { | ||
|
|
@@ -251,6 +261,48 @@ impl FFI_ArrowArray { | |
| } | ||
| } | ||
|
|
||
| /// Returns the producer-provided release callback, if any. | ||
| /// | ||
| /// Lets a consumer wrap release: save this callback, install its own with | ||
| /// [`FFI_ArrowArray::set_release`], and chain back to it on drop. See | ||
| /// <https://github.com/apache/arrow-rs/issues/9771>. | ||
| pub fn release(&self) -> Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowArray)> { | ||
| self.release | ||
| } | ||
|
|
||
| /// Returns the opaque producer-provided private data pointer. | ||
| /// | ||
| /// See [`FFI_ArrowArray::release`] for the intended use. | ||
| pub fn private_data(&self) -> *mut c_void { | ||
| self.private_data | ||
| } | ||
|
|
||
| /// Replaces the release callback, returning the previous one. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// [`Drop`] calls this callback with a pointer to `self`. The new callback | ||
| /// must correctly release this array (usually by chaining to the returned | ||
| /// one) and must match the [`FFI_ArrowArray::private_data`] it reads. A | ||
| /// wrong callback is undefined behavior on drop. | ||
| pub unsafe fn set_release( | ||
| &mut self, | ||
| release: Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowArray)>, | ||
| ) -> Option<unsafe extern "C" fn(arg1: *mut FFI_ArrowArray)> { | ||
| std::mem::replace(&mut self.release, release) | ||
| } | ||
|
|
||
| /// Replaces the private data pointer, returning the previous one. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// The old pointer is returned without being freed; the caller owns it from | ||
| /// here. The new pointer must match what the current | ||
| /// [`FFI_ArrowArray::release`] callback expects. | ||
| pub unsafe fn set_private_data(&mut self, private_data: *mut c_void) -> *mut c_void { | ||
| std::mem::replace(&mut self.private_data, private_data) | ||
| } | ||
|
|
||
| /// the length of the array | ||
| #[inline] | ||
| pub fn len(&self) -> usize { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.