Full Gemini Codebase Audit Report Appendix
Unsafe Rust Review: arrow_schema (v57)
Overall Safety Assessment
The arrow_schema crate contains a small amount of unsafe Rust code, entirely located in src/ffi.rs to support the Arrow C Data Interface. The density of unsafe code is low, but the risk is high because several safe public APIs wrap unsafe operations without adequate validation, leading to critical soundness bugs. Furthermore, safety documentation is almost entirely absent: there are virtually no # Safety sections for unsafe functions, and the few // SAFETY: comments are informal, lacking the logical rigor required by the review guidelines.
Critical Findings
Undefined Behavior / Memory Corruption in FFI_ArrowSchema::with_metadata 🔴 ⚠️
The public, safe builder method FFI_ArrowSchema::with_metadata is unsound and can be used to trigger immediate undefined behavior in safe Rust code.
Explanation
The method is defined as:
pub fn with_metadata<I, S>(mut self, metadata: I) -> Result<Self, ArrowError>
where
I: IntoIterator<Item = (S, S)>,
S: AsRef<str>,
{
...
unsafe {
let mut private_data = Box::from_raw(self.private_data as *mut SchemaPrivateData);
private_data.metadata = new_metadata;
self.private_data = Box::into_raw(private_data) as *mut c_void;
}
Ok(self)
}
If this method is called on a schema constructed via FFI_ArrowSchema::empty(), self.private_data is a null pointer. The code passes this null pointer directly to Box::from_raw, which violates the standard library safety precondition (the pointer must be non-null and allocated by the global allocator with the appropriate layout). This triggers undefined behavior (manifesting as a abort/panic in debug mode with standard library checks).
Furthermore, if the schema was imported from C via from_raw, self.private_data contains an opaque pointer provided by the foreign C producer. Calling with_metadata on such a schema will cast this opaque pointer to a SchemaPrivateData and attempt to drop it or reallocate it, causing type confusion and memory corruption.
Reproduction
#[test]
fn test_empty_with_metadata_ub() {
let schema = FFI_ArrowSchema::empty();
// This will call Box::from_raw(null) inside `with_metadata`, triggering UB.
let _ = schema.with_metadata([("key", "value")]);
}
Recommended Fix
Make the method validate that the schema was constructed locally and is initialized:
if self.private_data.is_null() {
return Err(ArrowError::CDataInterface(
"Cannot set metadata on an empty or uninitialized schema".to_string(),
));
}
if self.release != Some(release_schema) {
return Err(ArrowError::CDataInterface(
"Cannot set metadata on schema with external release callback".to_string(),
));
}
unsafe {
let mut private_data = Box::from_raw(self.private_data as *mut SchemaPrivateData);
private_data.metadata = new_metadata;
self.private_data = Box::into_raw(private_data) as *mut c_void;
}
Fishy Findings
None.
Missing Safety Comments
The following unsafe blocks and functions lack safety comments or have comments that are not structured as proofs:
1. FFI_ArrowSchema::from_raw 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
pub unsafe fn from_raw(schema: *mut FFI_ArrowSchema) -> Self {
unsafe { std::ptr::replace(schema, Self::empty()) }
}
2. FFI_ArrowSchema::format & name 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
pub fn format(&self) -> &str {
assert!(!self.format.is_null());
// safe because the lifetime of `self.format` equals `self`
unsafe { CStr::from_ptr(self.format) }
.to_str()
.expect("The external API has a non-utf8 as format")
}
-
Missing safety comment: The comments are brief and do not explain why it is safe to dereference the raw pointer (i.e. validity, null-termination).
-
Proposed safety comment for format:
// SAFETY:
// - `self.format` is checked to be non-null.
// - The provider of `FFI_ArrowSchema` guarantees that `format` points to a valid,
// null-terminated UTF-8 string that remains valid for the lifetime of `self`.
3. FFI_ArrowSchema::child 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
pub fn child(&self, index: usize) -> &Self {
assert!(index < self.n_children as usize);
unsafe { self.children.add(index).as_ref().unwrap().as_ref().unwrap() }
}
4. FFI_ArrowSchema::dictionary 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
pub fn dictionary(&self) -> Option<&Self> {
unsafe { self.dictionary.as_ref() }
}
5. FFI_ArrowSchema::metadata (helper functions) 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
fn next_four_bytes(buffer: *const u8, pos: &mut isize) -> [u8; 4] {
let out = unsafe {
[
*buffer.offset(*pos),
*buffer.offset(*pos + 1),
*buffer.offset(*pos + 2),
*buffer.offset(*pos + 3),
]
};
*pos += 4;
out
}
6. Drop for FFI_ArrowSchema 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
impl Drop for FFI_ArrowSchema {
fn drop(&mut self) {
match self.release {
None => (),
Some(release) => unsafe { release(self) },
};
}
}
7. unsafe impl Send for FFI_ArrowSchema 🟡
- Priority: 🟡 Low
- Bug Type: Missing Safety Documentation
unsafe impl Send for FFI_ArrowSchema {}
Note
This finding was identified during an agentic unsafe Rust code review performed by Gemini AI, followed by human review and verification.
The Issue
FFI_ArrowSchema::empty()initializesprivate_datato null.arrow-rs/arrow-schema/src/ffi.rs
Line 268 in 1583c8e
with_metadatacallsBox::from_rawon this value (unconditionally, I don't see an early return here)arrow-rs/arrow-schema/src/ffi.rs
Lines 232 to 236 in 1583c8e
Minimal Reproduction
Suggested Fix
Apply necessary runtime bounds checks or formalize the
unsafe fnsafety contract pre-conditions.Note
The full audit report below also contains additional minor findings (such as missing safety comments or undocumented FFI assumptions) that are probably worth fixing as well but not the primary goal of this issue. The audit report has not been human-reviewed, it may contain misleading claims.
Full Gemini Codebase Audit Report Appendix
Unsafe Rust Review:
arrow_schema(v57)Overall Safety Assessment
The
arrow_schemacrate contains a small amount of unsafe Rust code, entirely located insrc/ffi.rsto support the Arrow C Data Interface. The density of unsafe code is low, but the risk is high because several safe public APIs wrap unsafe operations without adequate validation, leading to critical soundness bugs. Furthermore, safety documentation is almost entirely absent: there are virtually no# Safetysections for unsafe functions, and the few// SAFETY:comments are informal, lacking the logical rigor required by the review guidelines.Critical Findings
Undefined Behavior / Memory Corruption in⚠️
FFI_ArrowSchema::with_metadata🔴Priority: 🔴 High
Threat Vector:⚠️ Accidental Misuse
Bug Type: Null Pointer Dereference
The public, safe builder method
FFI_ArrowSchema::with_metadatais unsound and can be used to trigger immediate undefined behavior in safe Rust code.Explanation
The method is defined as:
If this method is called on a schema constructed via
FFI_ArrowSchema::empty(),self.private_datais a null pointer. The code passes this null pointer directly toBox::from_raw, which violates the standard library safety precondition (the pointer must be non-null and allocated by the global allocator with the appropriate layout). This triggers undefined behavior (manifesting as a abort/panic in debug mode with standard library checks).Furthermore, if the schema was imported from C via
from_raw,self.private_datacontains an opaque pointer provided by the foreign C producer. Callingwith_metadataon such a schema will cast this opaque pointer to aSchemaPrivateDataand attempt to drop it or reallocate it, causing type confusion and memory corruption.Reproduction
Recommended Fix
Make the method validate that the schema was constructed locally and is initialized:
Fishy Findings
None.
Missing Safety Comments
The following unsafe blocks and functions lack safety comments or have comments that are not structured as proofs:
1.
FFI_ArrowSchema::from_raw🟡Missing safety documentation: The function has
# Safetydocumentation for the caller, but the unsafe block insidefrom_rawlacks a// SAFETY:comment explaining whystd::ptr::replaceis sound given those preconditions.Proposed safety comment:
2.
FFI_ArrowSchema::format&name🟡Missing safety comment: The comments are brief and do not explain why it is safe to dereference the raw pointer (i.e. validity, null-termination).
Proposed safety comment for
format:3.
FFI_ArrowSchema::child🟡Missing safety comment: No safety comment justifies the unsafe operations (pointer arithmetic and double dereferencing).
Proposed safety comment:
4.
FFI_ArrowSchema::dictionary🟡Missing safety comment: No safety comment justifies the pointer dereference.
Proposed safety comment:
5.
FFI_ArrowSchema::metadata(helper functions) 🟡Missing safety comment: No safety comment justifies the pointer arithmetic and dereferences.
Proposed safety comment:
6.
Drop for FFI_ArrowSchema🟡Missing safety comment: No safety comment justifying the invocation of the C function pointer.
Proposed safety comment:
7.
unsafe impl Send for FFI_ArrowSchema🟡Missing safety comment: No comment explaining why thread safety holds.
Proposed safety comment: