security: deny implicit unsafe ops in native crate
Jaime Fournier
6e8fb5bd9ea4b872af3ab02c111702983bde768e
--- a/docs/ffi-audit.md +++ b/docs/ffi-audit.md @@ -52,6 +52,10 @@ current release gate has a machine-readable count of Scheme FFI sites, native C/Rust files, Rust C ABI exports, pointer/width-sensitive bindings, blocking candidate calls, and unsafe Rust sites. +The native Rust crate now denies `unsafe_op_in_unsafe_fn`, so an unsafe +function body no longer grants implicit permission for unsafe operations. Each +unsafe operation still has to live inside an explicit `unsafe { ... }` block. + Remaining work before closing K3-P1-01: - Review every `foreign-procedure`, `foreign-callable`, and `define-ftype` @@ -60,8 +64,7 @@ Remaining work before closing K3-P1-01: nonblocking. - Add targeted scanner rules for null-return checks, integer width confusion, caller-supplied length trust, and bytevector pointer lifetime hazards. -- Add `SAFETY:` invariant comments near Rust unsafe sites and enable - `#![deny(unsafe_op_in_unsafe_fn)]` where supported. +- Add `SAFETY:` invariant comments near Rust unsafe sites. - Reconcile the 231 Rust `#[no_mangle]` exports with live Scheme callers and unexport or document unused exports. - Keep `vendor/jsqlite` in the TCB until consumers move to the Rust sqlite --- a/docs/kimi3-security-recommmendations.md +++ b/docs/kimi3-security-recommmendations.md @@ -597,9 +597,11 @@ not started." - **Status:** started. `docs/ffi-audit.md` and `tools/ffi-audit-report.ss` now provide a reproducible inventory for Scheme FFI sites, Rust C ABI exports, pointer/width-sensitive bindings, blocking candidates, and unsafe - Rust sites. Remaining work: per-binding verdicts, scanner rules for - null/width/bounds/GC-safety hazards, unsafe invariant comments, export - shrinking/justification, and the `vendor/jsqlite` CVE/replacement decision. + Rust sites. `jerboa-native-rs` now denies `unsafe_op_in_unsafe_fn`, so unsafe + function bodies do not implicitly permit unsafe operations. Remaining work: + per-binding verdicts, scanner rules for null/width/bounds/GC-safety hazards, + unsafe invariant comments, export shrinking/justification, and the + `vendor/jsqlite` CVE/replacement decision. ### K3-P1-02 — TOCTOU-safe filesystem capability checks **Serves:** G2. **Effort:** 1 week. --- a/jerboa-native-rs/src/http_parse.rs +++ b/jerboa-native-rs/src/http_parse.rs @@ -27,8 +27,10 @@ pub unsafe extern "C" fn jerboa_http_parse(buf: *const u8, buf_len: usize, out: return -1; } - let data = std::slice::from_raw_parts(buf, buf_len); - let out_slice = std::slice::from_raw_parts_mut(out, PARSE_OUT_SIZE); + // SAFETY: null pointers are rejected above; the FFI caller must pass + // readable input bytes and a writable PARSE_OUT_SIZE output buffer. + let data = unsafe { std::slice::from_raw_parts(buf, buf_len) }; + let out_slice = unsafe { std::slice::from_raw_parts_mut(out, PARSE_OUT_SIZE) }; let mut headers_storage = [httparse::EMPTY_HEADER; MAX_HEADERS]; let mut req = httparse::Request::new(&mut headers_storage); @@ -116,5 +118,7 @@ pub unsafe extern "C" fn jerboa_writev2( }, ]; let count = if use_two { 2 } else { 1 }; - libc::writev(fd, iovs.as_ptr(), count) + // SAFETY: buf1 is non-null with len1 > 0; buf2 is included only when + // non-null with len2 > 0; iovs points to count initialized entries. + unsafe { libc::writev(fd, iovs.as_ptr(), count) } } --- a/jerboa-native-rs/src/lib.rs +++ b/jerboa-native-rs/src/lib.rs @@ -1,3 +1,5 @@ +#![deny(unsafe_op_in_unsafe_fn)] + mod antidebug; mod aproc; mod compress; --- a/jerboa-native-rs/src/secure_mem.rs +++ b/jerboa-native-rs/src/secure_mem.rs @@ -17,7 +17,9 @@ fn round_up_to_page(size: usize, page_size: usize) -> Option<usize> { unsafe fn secure_zero(ptr: *mut u8, size: usize) { for i in 0..size { - ptr.add(i).write_volatile(0); + // SAFETY: caller guarantees ptr points to a writable allocation of at + // least size bytes; i is bounded by 0..size. + unsafe { ptr.add(i).write_volatile(0) }; } std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst); }