165 lines
3.9 KiB
Markdown
165 lines
3.9 KiB
Markdown
---
|
|
id: ffi-12
|
|
original_id: P.UNS.FFI.12
|
|
level: P
|
|
impact: MEDIUM
|
|
---
|
|
|
|
# Document Invariant Assumptions for C-Provided Parameters
|
|
|
|
## Summary
|
|
|
|
When receiving parameters from C, document what invariants you assume (non-null, alignment, validity, lifetime) and verify them when possible.
|
|
|
|
## Rationale
|
|
|
|
- C doesn't enforce invariants at compile time
|
|
- Rust code needs to validate or document assumptions
|
|
- Debugging FFI bugs is hard without clear documentation
|
|
|
|
## Bad Example
|
|
|
|
```rust
|
|
// DON'T: Undocumented assumptions
|
|
extern "C" {
|
|
fn get_data() -> *mut Data;
|
|
}
|
|
|
|
fn bad_use() -> &'static Data {
|
|
let ptr = unsafe { get_data() };
|
|
// Assumes:
|
|
// - ptr is non-null (not documented)
|
|
// - ptr is aligned (not checked)
|
|
// - Data is valid (not verified)
|
|
// - Lifetime is 'static (just guessing)
|
|
unsafe { &*ptr }
|
|
}
|
|
|
|
// DON'T: Silent assumptions in function signature
|
|
#[no_mangle]
|
|
pub extern "C" fn process(data: *const Data, len: usize) {
|
|
// What if data is null?
|
|
// What if len is wrong?
|
|
// What if data contains invalid Data?
|
|
let slice = unsafe {
|
|
std::slice::from_raw_parts(data, len)
|
|
};
|
|
}
|
|
```
|
|
|
|
## Good Example
|
|
|
|
```rust
|
|
/// Retrieves data from the C library.
|
|
///
|
|
/// # Invariants Assumed from C
|
|
///
|
|
/// - Returns a non-null pointer on success, null on failure
|
|
/// - Returned pointer is valid for the lifetime of the library
|
|
/// - Returned pointer is aligned for `Data`
|
|
/// - The `Data` struct is fully initialized
|
|
extern "C" {
|
|
fn get_data() -> *mut Data;
|
|
}
|
|
|
|
fn documented_use() -> Option<&'static Data> {
|
|
let ptr = unsafe { get_data() };
|
|
|
|
// Verify what we can
|
|
if ptr.is_null() {
|
|
return None;
|
|
}
|
|
|
|
// Document what we can't verify
|
|
// SAFETY:
|
|
// - Non-null: checked above
|
|
// - Aligned: documented in C library docs
|
|
// - Valid: C library guarantees initialized Data
|
|
// - Lifetime: C library guarantees static lifetime
|
|
Some(unsafe { &*ptr })
|
|
}
|
|
|
|
/// Processes data provided by C caller.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// - `data`: Must be non-null, aligned for `Data`, and point to `len` valid `Data` items
|
|
/// - `len`: Number of items. Must not exceed `isize::MAX / size_of::<Data>()`
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// - `0` on success
|
|
/// - `-1` if `data` is null
|
|
/// - `-2` if `len` is invalid
|
|
///
|
|
/// # Thread Safety
|
|
///
|
|
/// This function is thread-safe. The `data` array must not be mutated during the call.
|
|
#[no_mangle]
|
|
pub extern "C" fn process_documented(data: *const Data, len: usize) -> i32 {
|
|
// Verify invariants we can check
|
|
if data.is_null() {
|
|
return -1;
|
|
}
|
|
|
|
if len > isize::MAX as usize / std::mem::size_of::<Data>() {
|
|
return -2;
|
|
}
|
|
|
|
// SAFETY:
|
|
// - Non-null: checked above
|
|
// - Aligned: documented requirement for caller
|
|
// - Valid for len items: documented requirement for caller
|
|
// - Not mutated: documented thread safety requirement
|
|
let slice = unsafe { std::slice::from_raw_parts(data, len) };
|
|
|
|
for item in slice {
|
|
// process...
|
|
}
|
|
|
|
0
|
|
}
|
|
```
|
|
|
|
## Documentation Template
|
|
|
|
```rust
|
|
/// Brief description.
|
|
///
|
|
/// # Parameters
|
|
///
|
|
/// - `param`: Description, constraints (non-null, aligned, etc.)
|
|
///
|
|
/// # Invariants Assumed
|
|
///
|
|
/// The following invariants are assumed and NOT verified:
|
|
/// - Invariant 1: explanation
|
|
/// - Invariant 2: explanation
|
|
///
|
|
/// The following invariants ARE verified at runtime:
|
|
/// - Verified 1: how it's checked
|
|
///
|
|
/// # Safety (for unsafe fn)
|
|
///
|
|
/// Caller must ensure:
|
|
/// - Requirement 1
|
|
/// - Requirement 2
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns error code when:
|
|
/// - Condition 1: error code
|
|
```
|
|
|
|
## Checklist
|
|
|
|
- [ ] Have I documented all assumptions about C parameters?
|
|
- [ ] Which invariants can I verify at runtime?
|
|
- [ ] Which must I trust the C caller to uphold?
|
|
- [ ] Have I documented error conditions and return values?
|
|
|
|
## Related Rules
|
|
|
|
- `safety-02`: Verify safety invariants
|
|
- `safety-10`: Document safety requirements
|