Prefer Reentrant Versions of C-API or Syscalls

2026-08-30 浏览 (1)


id: mem-04 original_id: P.UNS.MEM.04 level: P impact: HIGH

Prefer Reentrant Versions of C-API or Syscalls

Summary

When calling C functions or system calls, use reentrant (_r) versions to avoid data races from global state.

Rationale

Many C library functions use static buffers or global state, making them unsafe in multithreaded programs. Reentrant versions use caller-provided buffers instead.

Bad Example

use std::ffi::CStr;

extern "C" {
    fn strtok(s: *mut i8, delim: *const i8) -> *mut i8;
    fn localtime(time: *const i64) -> *mut Tm;
    fn rand() -> i32;
}

// DON'T: Use non-reentrant functions
fn bad_tokenize(s: &mut [i8]) {
    unsafe {
        let delim = b" \0".as_ptr() as *const i8;
        // strtok uses static buffer - not thread-safe!
        let token = strtok(s.as_mut_ptr(), delim);
    }
}

fn bad_time() {
    unsafe {
        let now: i64 = 0;
        // localtime returns pointer to static buffer
        let tm = localtime(&now);  // Data race if called from multiple threads!
    }
}

fn bad_random() -> i32 {
    // rand() uses global state - not thread-safe
    unsafe { rand() }
}

Good Example

extern "C" {
    fn strtok_r(s: *mut i8, delim: *const i8, saveptr: *mut *mut i8) -> *mut i8;
    fn localtime_r(time: *const i64, result: *mut Tm) -> *mut Tm;
    fn rand_r(seed: *mut u32) -> i32;
}

// DO: Use reentrant versions
fn good_tokenize(s: &mut [i8]) {
    unsafe {
        let delim = b" \0".as_ptr() as *const i8;
        let mut saveptr: *mut i8 = std::ptr::null_mut();
        // strtok_r uses caller-provided saveptr
        let token = strtok_r(s.as_mut_ptr(), delim, &mut saveptr);
    }
}

fn good_time() {
    unsafe {
        let now: i64 = 0;
        let mut result: Tm = std::mem::zeroed();
        // localtime_r writes to caller-provided buffer
        localtime_r(&now, &mut result);
    }
}

fn good_random(seed: &mut u32) -> i32 {
    // rand_r uses caller-provided seed
    unsafe { rand_r(seed) }
}

// BETTER: Use Rust standard library
fn best_time() {
    use std::time::SystemTime;
    let now = SystemTime::now();  // Thread-safe!
}

fn best_random() -> u32 {
    use rand::Rng;
    rand::thread_rng().gen()  // Thread-safe!
}

Common Non-Reentrant Functions

Non-ReentrantReentrantRust Alternative
strtokstrtok_rstr::split
localtimelocaltime_rchrono crate
gmtimegmtime_rchrono crate
ctimectime_rchrono crate
randrand_rrand crate
strerrorstrerror_rstd::io::Error
getenvNone (inherent race)std::env::var (not atomic)
readdirreaddir_rstd::fs::read_dir
gethostbynamegetaddrinfostd::net::ToSocketAddrs

Checklist

  • Am I calling a C function that might use global state?
  • Is there a _r reentrant version available?
  • Is there a Rust standard library alternative?
  • If neither, do I need synchronization?

Related Rules

  • ffi-10: Exported functions must be thread-safe
  • ptr-01: Don't share raw pointers across threads

你可能感兴趣的文章

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/UPd6qegm