cargo / nix / audit
cargo : nix @ 0.29.0
PE Patrick Elsen signed 2026-05-28 published 2026-05-28

Claims

build-exec-deterministicbuild-exec-minimalbuild-exec-no-networkbuild-exec-no-write-outbuild-exec-safeconcurrency-documentedconcurrency-safeenvironment-safeexec-safefilesystem-safehas-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignnetwork-safeunsafe-documentedunsafe-minimalunsafe-safeuses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

nix 0.29.0 wraps the Unix/POSIX syscall API over libc with about 825 focused unsafe FFI sites. Reviewed high-risk paths (cmsg decode, sockaddr length handling, mmap, fork/exec, signal transmutes) uphold their libc preconditions; source is byte-identical to VCS and build.rs is inert. No findings.

Report

Subject

nix 0.29.0 is a set of Rust wrappers over the Unix/POSIX system-call API exposed by the libc crate. It covers sockets, signals, process control (fork, exec, wait, clone), memory mapping, file and directory operations, terminal control, scheduling, ptrace, and dozens of smaller POSIX surfaces. The public API mirrors C headers module-by-module (sys::socket, sys::signal, unistd, sys::mman, and so on) and translates C error returns into a Result<T, Errno>. Every capability beyond a small core is feature-gated, and platform differences are handled by extensive cfg gating across Linux, the BSDs, the Apple targets, the Solaris-like systems, and others. It is one of the most widely depended-on crates in the Rust ecosystem for direct syscall access.

Methodology

Tooling: openvet 0.6.0, ripgrep, diff, git, wc. I read Cargo.toml, Cargo.toml.orig, and build.rs in full, then surveyed the source with ripgrep to map where unsafe, extern "C", FFI, and the various I/O surfaces live. The crate is roughly 32.5K lines across 62 files with about 825 occurrences of unsafe, a very high density driven by one focused libc FFI call per wrapper. Rather than read all 825 sites, I read the representative high-risk paths end-to-end: the NixPath/with_nix_path CStr conversion in lib.rs, the cmsg decode and sockaddr from_raw machinery in sys/socket, the recvmsg/sendmsg msghdr packing, errno sentinel handling, the SigAction handler transmutes in sys/signal, fork/exec/pipe/getgroups in unistd, the mmap family in sys/mman, the WaitStatus/siginfo decode in sys/wait, the clone callback in sched, and clearenv in env. I diffed contents against vcs.

Results

Every file under contents/src is byte-identical to the VCS checkout; only Cargo.toml differs, from cargo's publish-time normalisation. The VCS tree is a full git repository.

The crate is built almost entirely on unsafe FFI, so uses-unsafe holds. The unsafe is minimal in the sense that each block is a single libc call or the pointer/length setup feeding one (unsafe-minimal), and it is documented through // SAFETY:/Safe because comments and # Safety doc sections, with the crate compiled under #![deny(unsafe_op_in_unsafe_fn)] (unsafe-documented). Across the reviewed paths the FFI preconditions hold (unsafe-safe): with_nix_path bounds its stack buffer and rejects interior NULs (lib.rs:287-330); cmsg decoding reads every payload with ptr::read_unaligned and bounds-checks the SO_EE_OFFENDER address (sys/socket/mod.rs:855-1016); sockaddr from_raw validates length against the target struct size before copying and re-checks family and length in its accessors (sys/socket/addr.rs:1132-1232); the mmap family checks MAP_FAILED before building a NonNull (sys/mman.rs:395-449); and Errno::result applies the standard -1/MAP_FAILED/SIG_ERR sentinel-to-errno translation.

The socket wrappers issue raw network syscalls and validate the sockaddr length and family before exposing typed views, justifying uses-network and network-safe; nix sits below the protocol layer and has no TLS-by-default notion, so network-secure was not asserted. The exec* family launches process images in argv form from caller-supplied CStr values with a NULL-terminated array built by to_exec_array (uses-exec, exec-safe), and fork/clone are unsafe fns whose async-signal-safety and stack-overflow contracts are documented (unistd.rs:808-855), supporting uses-concurrency, concurrency-safe, and concurrency-documented. nix wraps libc's threading and process primitives rather than implementing its own, so impl-concurrency is false. Path-taking wrappers forward caller paths straight to libc without adding traversal logic (uses-filesystem, filesystem-safe), and clearenv only clears the environment on request, documenting its thread-safety precondition and never enumerating or transmitting the environment (env.rs:41-63), supporting uses-environment and environment-safe.

build.rs only invokes cfg_aliases! and emits cargo:rustc-check-cfg lines; it reads no files, makes no network calls, and spawns nothing (has-build-exec, build-exec-safe, build-exec-deterministic, build-exec-no-network, build-exec-no-write-out, build-exec-minimal). The crate ships no precompiled artifacts and no install hook (has-binaries, has-install-exec). It contains unit tests in src (chiefly sockaddr round-trip and size checks in addr.rs) and a substantial integration suite under test/ (has-unit-tests, has-integration-tests), but no fuzz or property-test harness in the published crate (has-fuzz-tests, has-property-tests); for that reason unsafe-tested was not asserted. nix performs no cryptography (the only crypto-named symbols are kernel-TLS socket-option enums), runs no interpreter or JIT, and implements no parser, protocol, data structure, or general algorithm of its own, so uses-crypto, uses-jit, uses-interpreter, impl-crypto, impl-parser, impl-interpreter, impl-jit, impl-protocol, impl-datastructure, and impl-algorithm are all false. A scan for obfuscation, encoded blobs, suspicious endpoints, and telemetry found only legitimate documentation URLs, supporting is-benign. No findings were recorded.

Conclusion

nix 0.29.0 is a thin, heavily cfg-gated FFI layer with about 825 unsafe sites, each a focused libc call. The reviewed high-risk paths, cmsg decode, sockaddr length handling, msghdr packing, the mmap family, fork/exec, signal-handler transmutes, and the CStr boundary, uphold their libc preconditions: unaligned reads where alignment is not guaranteed, length-and-family validation before copying kernel data, MAP_FAILED checks before NonNull construction, and consistent sentinel-to-errno translation. Functions whose safety cannot be enforced at the type level (fork, clone, sigaction, clearenv) are unsafe fns with documented contracts. Source is byte-identical to VCS, the build script is inert, and no malicious or obfuscated code was found. The audit recorded no findings. The unsafe review was representative rather than exhaustive across all 825 sites, and the published crate carries no fuzz or property-test harness.

Findings

No findings.

Annotations(7)

build.rs

build.rs invokes cfg_aliases::cfg_aliases! to register per-OS cfg aliases (bsd, linux_android, apple_targets, etc.) and emits cargo:rustc-check-cfg lines for nix's custom cfg names. It reads no files, makes no network requests, spawns no processes, and writes nothing outside cargo's own cfg output channel. Its output is a pure function of the target triple.

Justifies has-build-exec, build-exec-safe, build-exec-deterministic, build-exec-no-network, build-exec-no-write-out, and build-exec-minimal.

src/env.rs

src/env.rs, line 41-63

pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
    cfg_if! {
        if #[cfg(any(linux_android,
                     target_os = "fuchsia",
                     target_os = "wasi",
                     target_env = "uclibc",
                     target_os = "emscripten"))] {
            let ret = unsafe { libc::clearenv() };
        } else {
            use std::env;
            for (name, _) in env::vars_os() {
                env::remove_var(name);
            }
            let ret = 0;
        }
    }

    if ret == 0 {
        Ok(())
    } else {
        Err(ClearEnvError)
    }
}

clearenv is an unsafe fn. Its doc comment states the precondition that no other thread may access the process environment during the call and that no raw pointer into libc's environ may be held. On platforms with libc::clearenv it forwards to it; elsewhere it iterates env::vars_os and removes each entry. It only clears the environment at the caller's explicit request and does not enumerate or transmit environment contents elsewhere.

Justifies uses-environment and environment-safe.

src/lib.rs

src/lib.rs, line 287-330

    fn with_nix_path<T, F>(&self, f: F) -> Result<T>
    where
        F: FnOnce(&CStr) -> T,
    {
        // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
        // longer than ~300 bytes. See the the PR description to get stats for your own machine.
        // https://github.com/nix-rust/nix/pull/1656
        //
        // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
        // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
        const MAX_STACK_ALLOCATION: usize = 1024;

        if self.len() >= MAX_STACK_ALLOCATION {
            return with_nix_path_allocating(self, f);
        }

        let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
        let buf_ptr = buf.as_mut_ptr().cast();

        unsafe {
            ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
            buf_ptr.add(self.len()).write(0);
        }

        match CStr::from_bytes_with_nul(unsafe {
            slice::from_raw_parts(buf_ptr, self.len() + 1)
        }) {
            Ok(s) => Ok(f(s)),
            Err(_) => Err(Errno::EINVAL),
        }
    }
}

#[cold]
#[inline(never)]
fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
where
    F: FnOnce(&CStr) -> T,
{
    match CString::new(from) {
        Ok(s) => Ok(f(&s)),
        Err(_) => Err(Errno::EINVAL),
    }
}

with_nix_path for [u8] converts a byte path into a NUL-terminated CStr for FFI. For inputs shorter than 1024 bytes it uses a stack MaybeUninit<[u8; 1024]>: it copies self.len() bytes with ptr::copy_nonoverlapping, then writes a NUL at offset self.len() (at most 1023, in bounds), and validates via CStr::from_bytes_with_nul over len + 1 bytes. An interior NUL makes the conversion return Errno::EINVAL rather than truncating. Inputs of 1024 bytes or more take the heap fallback with_nix_path_allocating, which uses CString::new. The 1024-byte cap also stays below a page to avoid a stack probe frame.

Justifies uses-unsafe and unsafe-safe.

src/sys/mman.rs

src/sys/mman.rs, line 395-449

pub unsafe fn mmap<F: AsFd>(
    addr: Option<NonZeroUsize>,
    length: NonZeroUsize,
    prot: ProtFlags,
    flags: MapFlags,
    f: F,
    offset: off_t,
) -> Result<NonNull<c_void>> {
    let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);

    let fd = f.as_fd().as_raw_fd();
    let ret = unsafe {
        libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset)
    };

    if ret == libc::MAP_FAILED {
        Err(Errno::last())
    } else {
        // SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
        // will be non-null here.
        Ok(unsafe { NonNull::new_unchecked(ret) })
    }
}

/// Create an anonymous memory mapping.
///
/// This function is a wrapper around [`mmap`]:
/// `mmap(ptr, len, prot, MAP_ANONYMOUS | flags, -1, 0)`.
///
/// # Safety
///
/// See the [`mmap(2)`] man page for detailed requirements.
///
/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
pub unsafe fn mmap_anonymous(
    addr: Option<NonZeroUsize>,
    length: NonZeroUsize,
    prot: ProtFlags,
    flags: MapFlags,
) -> Result<NonNull<c_void>> {
    let ptr = addr.map_or(std::ptr::null_mut(), |a| a.get() as *mut c_void);

    let flags = MapFlags::MAP_ANONYMOUS | flags;
    let ret = unsafe {
        libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), -1, 0)
    };

    if ret == libc::MAP_FAILED {
        Err(Errno::last())
    } else {
        // SAFETY: `libc::mmap` returns a valid non-null pointer or `libc::MAP_FAILED`, thus `ret`
        // will be non-null here.
        Ok(unsafe { NonNull::new_unchecked(ret) })
    }
}

The mman wrappers are unsafe fns that delegate their preconditions to the mmap(2)/mremap(2)/munmap(2) man pages via documented # Safety sections. Each constructor checks the libc return against MAP_FAILED and only calls NonNull::new_unchecked on the success path, so the returned NonNull<c_void> is never built from a failed or null mapping. length is a NonZeroUsize, ruling out the zero-length EINVAL case at the type level.

Justifies uses-unsafe, unsafe-safe, unsafe-documented, and unsafe-minimal.

src/sys/socket/addr.rs

src/sys/socket/addr.rs, line 1132-1232

impl SockaddrLike for SockaddrStorage {
    unsafe fn from_raw(
        addr: *const libc::sockaddr,
        l: Option<libc::socklen_t>,
    ) -> Option<Self>
    where
        Self: Sized,
    {
        if addr.is_null() {
            return None;
        }
        if let Some(len) = l {
            let ulen = len as usize;
            if ulen < offset_of!(libc::sockaddr, sa_data)
                || ulen > mem::size_of::<libc::sockaddr_storage>()
            {
                None
            } else {
                let mut ss: libc::sockaddr_storage = unsafe { mem::zeroed() };
                let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
                unsafe { ptr::copy(addr as *const u8, ssp, len as usize) };
                #[cfg(any(
                    linux_android,
                    target_os = "fuchsia",
                    solarish,
                ))]
                if i32::from(ss.ss_family) == libc::AF_UNIX {
                    // Safe because we UnixAddr is strictly smaller than
                    // SockaddrStorage, and we just initialized the structure.
                    unsafe {
                        (*(&mut ss as *mut libc::sockaddr_storage
                            as *mut UnixAddr))
                            .sun_len = len as u8;
                    }
                }
                Some(Self { ss })
            }
        } else {
            // If length is not available and addr is of a fixed-length type,
            // copy it.  If addr is of a variable length type and len is not
            // available, then there's nothing we can do.
            match unsafe { (*addr).sa_family as i32 } {
                #[cfg(linux_android)]
                libc::AF_ALG => unsafe {
                    AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
                },
                #[cfg(feature = "net")]
                libc::AF_INET => unsafe {
                    SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
                },
                #[cfg(feature = "net")]
                libc::AF_INET6 => unsafe {
                    SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
                },
                #[cfg(any(bsd, solarish, target_os = "haiku"))]
                #[cfg(feature = "net")]
                libc::AF_LINK => unsafe {
                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
                },
                #[cfg(linux_android)]
                libc::AF_NETLINK => unsafe {
                    NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl })
                },
                #[cfg(any(linux_android, target_os = "fuchsia"))]
                #[cfg(feature = "net")]
                libc::AF_PACKET => unsafe {
                    LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
                },
                #[cfg(all(feature = "ioctl", apple_targets))]
                libc::AF_SYSTEM => unsafe {
                    SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
                },
                #[cfg(any(linux_android, apple_targets))]
                libc::AF_VSOCK => unsafe {
                    VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
                },
                _ => None,
            }
        }
    }

    #[cfg(any(linux_android, target_os = "fuchsia", solarish))]
    fn len(&self) -> libc::socklen_t {
        match self.as_unix_addr() {
            // The UnixAddr type knows its own length
            Some(ua) => ua.len(),
            // For all else, we're just a boring SockaddrStorage
            None => mem::size_of_val(self) as libc::socklen_t,
        }
    }

    unsafe fn set_length(
        &mut self,
        new_length: usize,
    ) -> std::result::Result<(), SocketAddressLengthNotDynamic> {
        match self.as_unix_addr_mut() {
            Some(addr) => unsafe { addr.set_length(new_length) },
            None => Err(SocketAddressLengthNotDynamic),
        }
    }
}

SockaddrStorage::from_raw is the generic dispatcher that copies a raw sockaddr into nix's owned storage. When a length is supplied it rejects values below offsetof(sockaddr, sa_data) or above size_of::<sockaddr_storage>(), zeroes the destination, then copies exactly len bytes; the AF_UNIX branch records the length in the sun_len shadow field. When no length is supplied it dispatches on sa_family only for fixed-length address types. The per-family from_raw impls (UnixAddr, VsockAddr, and the rest) apply the same pattern: validate the length against the expected struct size, verify sa_family, then copy via ptr::copy/ptr::read_unaligned. The accessors! macro re-checks family and length before handing out a typed reference.

Justifies uses-unsafe, unsafe-safe, and network-safe.

src/sys/socket/mod.rs

src/sys/socket/mod.rs, line 855-1016

impl ControlMessageOwned {
    /// Decodes a `ControlMessageOwned` from raw bytes.
    ///
    /// This is only safe to call if the data is correct for the message type
    /// specified in the header. Normally, the kernel ensures that this is the
    /// case. "Correct" in this case includes correct length, alignment and
    /// actual content.
    // Clippy complains about the pointer alignment of `p`, not understanding
    // that it's being fed to a function that can handle that.
    #[allow(clippy::cast_ptr_alignment)]
    unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
    {
        let p = unsafe { CMSG_DATA(header) };
        // The cast is not unnecessary on all platforms.
        #[allow(clippy::unnecessary_cast)]
        let len = header as *const _ as usize + header.cmsg_len as usize
            - p as usize;
        match (header.cmsg_level, header.cmsg_type) {
            (libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
                let n = len / mem::size_of::<RawFd>();
                let mut fds = Vec::with_capacity(n);
                for i in 0..n {
                    unsafe {
                        let fdp = (p as *const RawFd).add(i);
                        fds.push(ptr::read_unaligned(fdp));
                    }
                }
                ControlMessageOwned::ScmRights(fds)
            },
            #[cfg(linux_android)]
            (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
                let cred: libc::ucred = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmCredentials(cred.into())
            }
            #[cfg(freebsdlike)]
            (libc::SOL_SOCKET, libc::SCM_CREDS) => {
                let cred: libc::cmsgcred = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmCreds(cred.into())
            }
            #[cfg(not(any(target_os = "aix", target_os = "haiku")))]
            (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
                let tv: libc::timeval = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
            },
            #[cfg(linux_android)]
            (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
                let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
            }
            #[cfg(target_os = "freebsd")]
            (libc::SOL_SOCKET, libc::SCM_REALTIME) => {
                let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmRealtime(TimeSpec::from(ts))
            }
            #[cfg(target_os = "freebsd")]
            (libc::SOL_SOCKET, libc::SCM_MONOTONIC) => {
                let ts: libc::timespec = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::ScmMonotonic(TimeSpec::from(ts))
            }
            #[cfg(linux_android)]
            (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
                let tp = p as *const libc::timespec;
                let ts: libc::timespec = unsafe { ptr::read_unaligned(tp) };
                let system = TimeSpec::from(ts);
                let ts: libc::timespec = unsafe { ptr::read_unaligned(tp.add(1)) };
                let hw_trans = TimeSpec::from(ts);
                let ts: libc::timespec = unsafe { ptr::read_unaligned(tp.add(2)) };
                let hw_raw = TimeSpec::from(ts);
                let timestamping = Timestamps { system, hw_trans, hw_raw };
                ControlMessageOwned::ScmTimestampsns(timestamping)
            }
            #[cfg(any(target_os = "freebsd", linux_android, apple_targets))]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
                let info = unsafe { ptr::read_unaligned(p as *const libc::in6_pktinfo) };
                ControlMessageOwned::Ipv6PacketInfo(info)
            }
            #[cfg(any(linux_android, apple_targets, target_os = "netbsd"))]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
                let info = unsafe { ptr::read_unaligned(p as *const libc::in_pktinfo) };
                ControlMessageOwned::Ipv4PacketInfo(info)
            }
            #[cfg(bsd)]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IP, libc::IP_RECVIF) => {
                let dl = unsafe { ptr::read_unaligned(p as *const libc::sockaddr_dl) };
                ControlMessageOwned::Ipv4RecvIf(dl)
            },
            #[cfg(bsd)]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
                let dl = unsafe { ptr::read_unaligned(p as *const libc::in_addr) };
                ControlMessageOwned::Ipv4RecvDstAddr(dl)
            },
            #[cfg(any(linux_android, target_os = "freebsd"))]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
                let dl = unsafe { ptr::read_unaligned(p as *const libc::sockaddr_in) };
                ControlMessageOwned::Ipv4OrigDstAddr(dl)
            },
            #[cfg(target_os = "linux")]
            #[cfg(feature = "net")]
            (libc::SOL_UDP, libc::UDP_GRO) => {
                let gso_size: i32 = unsafe { ptr::read_unaligned(p as *const _) };
                ControlMessageOwned::UdpGroSegments(gso_size)
            },
            #[cfg(any(linux_android, target_os = "fuchsia"))]
            (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
                let drop_counter = unsafe { ptr::read_unaligned(p as *const u32) };
                ControlMessageOwned::RxqOvfl(drop_counter)
            },
            #[cfg(linux_android)]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IP, libc::IP_RECVERR) => {
                let (err, addr) = unsafe { Self::recv_err_helper::<sockaddr_in>(p, len) };
                ControlMessageOwned::Ipv4RecvErr(err, addr)
            },
            #[cfg(linux_android)]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
                let (err, addr) = unsafe { Self::recv_err_helper::<sockaddr_in6>(p, len) };
                ControlMessageOwned::Ipv6RecvErr(err, addr)
            },
            #[cfg(any(linux_android, target_os = "freebsd"))]
            #[cfg(feature = "net")]
            (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
                let dl = unsafe { ptr::read_unaligned(p as *const libc::sockaddr_in6) };
                ControlMessageOwned::Ipv6OrigDstAddr(dl)
            },
            #[cfg(any(target_os = "linux"))]
            (libc::SOL_TLS, libc::TLS_GET_RECORD_TYPE) => {
                let content_type = unsafe { ptr::read_unaligned(p as *const u8) };
                ControlMessageOwned::TlsGetRecordType(content_type.into())
            },
            (_, _) => {
                let sl = unsafe { std::slice::from_raw_parts(p, len) };
                let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
                ControlMessageOwned::Unknown(ucmsg)
            }
        }
    }

    #[cfg(linux_android)]
    #[cfg(feature = "net")]
    #[allow(clippy::cast_ptr_alignment)]    // False positive
    unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
        let ee = p as *const libc::sock_extended_err;
        let err = unsafe { ptr::read_unaligned(ee) };

        // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
        // CMSG_DATA buffer.  For local errors, there is no address included in the control
        // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer.  So, we need to
        // validate that the address object is in-bounds before we attempt to copy it.
        let addrp = unsafe { libc::SO_EE_OFFENDER(ee) as *const T };

        if unsafe { addrp.offset(1) } as usize - (p as usize) > len {
            (err, None)
        } else {
            (err, Some(unsafe { ptr::read_unaligned(addrp) }))
        }
    }

ControlMessageOwned::decode_from is an unsafe fn that copies an ancillary message out of a kernel-filled cmsg buffer. It computes the payload length as cmsg_len - (CMSG_DATA - cmsghdr) and reads every payload through ptr::read_unaligned, which avoids alignment UB on platforms where cmsg payloads are not naturally aligned. recv_err_helper bounds-checks the SO_EE_OFFENDER address against p[..len] before copying it, returning None for local errors whose offender address points past the buffer. The catch-all Unknown branch builds a Vec<u8> from slice::from_raw_parts(p, len). The function's safety contract (the kernel guarantees length, alignment, and content correctness for the message type) is documented in its doc comment.

Justifies uses-unsafe, unsafe-safe, unsafe-documented, and network-safe.

src/unistd.rs

src/unistd.rs, line 808-855

fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
    use std::iter::once;
    args.iter()
        .map(|s| s.as_ref().as_ptr())
        .chain(once(ptr::null()))
        .collect()
}

/// Replace the current process image with a new one (see
/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
///
/// See the `::nix::unistd::execve` system call for additional details.  `execv`
/// performs the same action but does not allow for customization of the
/// environment for the new process.
#[inline]
pub fn execv<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
    let args_p = to_exec_array(argv);

    unsafe { libc::execv(path.as_ptr(), args_p.as_ptr()) };

    Err(Errno::last())
}

/// Replace the current process image with a new one (see
/// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
///
/// The execve system call allows for another process to be "called" which will
/// replace the current process image.  That is, this process becomes the new
/// command that is run. On success, this function will not return. Instead,
/// the new program will run until it exits.
///
/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice
/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element
/// in the `args` list is an argument to the new process. Each element in the
/// `env` list should be a string in the form "key=value".
#[inline]
pub fn execve<SA: AsRef<CStr>, SE: AsRef<CStr>>(
    path: &CStr,
    args: &[SA],
    env: &[SE],
) -> Result<Infallible> {
    let args_p = to_exec_array(args);
    let env_p = to_exec_array(env);

    unsafe { libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) };

    Err(Errno::last())
}

fork is an unsafe fn whose doc comment spells out the async-signal-safety constraints on the child (only async-signal-safe functions, no allocation). The exec* family builds its argv/envp via to_exec_array, which maps each CStr to its pointer and appends a NULL terminator; the borrowed argument slice keeps every CStr alive across the call, and on success the call does not return. Process images are launched in argv form rather than through a shell, and the program path and arguments are caller-supplied CStr values.

Justifies uses-unsafe, unsafe-safe, unsafe-documented, uses-exec, exec-safe, uses-concurrency, concurrency-safe, and concurrency-documented.