Validate Non-Robust External Values

2026-08-30 浏览 (1)


id: ffi-15 original_id: P.UNS.FFI.15 level: P impact: HIGH

Validate Non-Robust External Values

Summary

Data received from external sources (FFI, files, network) may be invalid. Validate before using it as Rust types with stricter invariants.

Rationale

  • External data can be malicious or corrupted
  • Rust types have invariants (e.g., valid UTF-8 for str)
  • Invalid data causes undefined behavior

Bad Example

// DON'T: Trust external data
extern "C" {
    fn get_status() -> u8;
}

#[derive(Debug)]
enum Status { Active = 0, Inactive = 1, Pending = 2 }

fn bad_convert() -> Status {
    let raw = unsafe { get_status() };
    // BAD: Assumes C returns valid enum value
    unsafe { std::mem::transmute(raw) }  // UB if raw > 2
}

// DON'T: Trust strings from C
fn bad_string(ptr: *const c_char) -> &str {
    let cstr = unsafe { CStr::from_ptr(ptr) };
    // BAD: Assumes valid UTF-8
    cstr.to_str().unwrap()
}

// DON'T: Trust size values
fn bad_size(ptr: *const u8, len: usize) -> Vec<u8> {
    // BAD: len could be huge, causing OOM
    // BAD: len could exceed actual data
    unsafe { std::slice::from_raw_parts(ptr, len) }.to_vec()
}

Good Example

// DO: Validate enum values
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
enum Status {
    Active = 0,
    Inactive = 1,
    Pending = 2,
}

impl TryFrom<u8> for Status {
    type Error = InvalidStatusError;

    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Status::Active),
            1 => Ok(Status::Inactive),
            2 => Ok(Status::Pending),
            _ => Err(InvalidStatusError(value)),
        }
    }
}

fn good_convert() -> Result<Status, InvalidStatusError> {
    let raw = unsafe { get_status() };
    Status::try_from(raw)  // Returns error for invalid values
}

// DO: Handle invalid UTF-8
fn good_string(ptr: *const c_char) -> Result<String, std::str::Utf8Error> {
    if ptr.is_null() {
        return Ok(String::new());
    }
    let cstr = unsafe { CStr::from_ptr(ptr) };
    cstr.to_str().map(|s| s.to_owned())
}

fn good_string_lossy(ptr: *const c_char) -> String {
    if ptr.is_null() {
        return String::new();
    }
    let cstr = unsafe { CStr::from_ptr(ptr) };
    cstr.to_string_lossy().into_owned()  // Replaces invalid UTF-8
}

// DO: Validate sizes
const MAX_REASONABLE_SIZE: usize = 100 * 1024 * 1024;  // 100 MB

fn good_size(ptr: *const u8, len: usize) -> Result<Vec<u8>, ValidationError> {
    if ptr.is_null() {
        return Err(ValidationError::NullPointer);
    }
    if len > MAX_REASONABLE_SIZE {
        return Err(ValidationError::SizeTooLarge);
    }

    // Still need to trust that ptr points to len valid bytes
    // Document this as a caller requirement
    let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
    Ok(slice.to_vec())
}

// DO: Use num_enum for safe enum conversion
// use num_enum::TryFromPrimitive;
//
// #[derive(TryFromPrimitive)]
// #[repr(u8)]
// enum Status { Active = 0, Inactive = 1, Pending = 2 }

Validation Patterns

External DataValidation
Enum discriminantMatch against valid values
StringCheck UTF-8 or use lossy conversion
Size/lengthCheck against maximum
PointerCheck for null
BooleanExplicit 0/1 check or treat any non-zero as true
FloatCheck for NaN, infinity if problematic

Checklist

  • Am I validating external enum values?
  • Am I handling potential invalid UTF-8?
  • Am I checking sizes against reasonable limits?
  • Am I using TryFrom instead of transmute?

Related Rules

  • ffi-12: Document invariant assumptions
  • safety-02: Verify safety invariants

你可能感兴趣的文章

Do Not Expose Raw Pointers in Public APIs

Unsafe Code Authors Must Verify Safety Invariants

Ensure Consistent Data Layout for Custom Types

Add Safety Section in Docs for Public Unsafe Functions

Do Not Use Union Variants Across Different Lifetimes

Use MaybeUninit for Uninitialized Memory

Ensure C-ABI Compatibility for Strings Between Rust and C

Do Not Dereference Pointers Cast to Misaligned Types

Use Dedicated Opaque Type Pointers Instead of c_void for C Opaque Types

Choose Appropriate Data Layout for Struct/Tuple/Enum

  • 所属分类: AI
  • 本文标签: rust
  • 版权声明: 本文链接 https://seaxiang.com/blog/Y8LCFytL