CodeVetter · Repo Unpack · Risk map
torvalds/linux
The highest-risk paths are the ones that cross privilege boundaries: module loading (ring-0 code injection gated by CAP_SYS_MODULE and signatures), the eBPF verifier, io_uring's single-fd multiplexer over dozens of opcodes, usermodehelper exec, seccomp filter evaluation, and every user-memory copy in syscall bodies. Concurrency hazards concentrate in the RCU path-walk retry ladder and the scheduler's rq-lock protocol. Boot is fail-hard: missing init or corrupt initmem panics the machine.
- Module loading is the most direct ring-0 injection path: may_init_module() requires CAP_SYS_MODULE and respects modules_disabled; module_sig_check enforces CONFIG_MODULE_SIG/MODULE_SIG_FORCE; elf_validity_ehdr rejects malformed images — a bypass anywhere here yields full kernel compromise.
- call_usermodehelper execs userspace binaries from kernel context with usermodehelper_bset/CAP_FULL_SET defaults; callers like request_module (kernel/module/kmod.c) make this a kernel->userspace escalation surface that must sanitize inputs.
- The eBPF verifier (kernel/bpf/verifier.c, ~21.5k lines) is the barrier between unprivileged/privileged userspace and in-kernel JIT'd code; bpf_prog_load -> bpf_check -> security_bpf_prog chains show the gating, and verifier bugs historically yield privilege escalation.
- io_uring multiplexes ~all VFS/socket/block operations through one fd via the io_issue_defs[] opcode table; BUG_ON checks in opdef.c enforce invariants at registration, but the breadth of reachable kernel code makes it a persistent CVE hotspot.
- seccomp evaluates user-supplied cBPF on every syscall via struct seccomp_filter trees linked through ->prev; filters are immutable once attached and the cache (seccomp_cache_check_allow) fast-paths 'allow' decisions — subtle bugs here weaken sandboxing.
- User-memory crossings are scattered and easy to get wrong: openat2 copy_struct_from_user for extensible open_how, vfs_read/vfs_write access_ok checks, put_user in kernel_clone for parent_tid — each is a potential info-leak/overflow if bounds slip.
- RCU path-walk retry semantics are fragile: filesystems and callers must tolerate -ECHILD/-ESTALE/-EOPENSTALE restart loops in do_file_open; code that caches dentry state across a restart risks use-after-free or TOCTOU.
- Boot integrity self-checks exist but only warn/panic: mark_readonly() under STRICT_KERNEL_RWX runs mark_rodata_ro, debug_checkwx and rodata_test; failures print warnings or panic, and CONFIG_STRICT_KERNEL_RWX can be disabled via the rodata= boot param.
- Failure mode for missing init is panic(): after trying /init, init=, CONFIG_DEFAULT_INIT, /sbin/init, /etc/init, /bin/init and /bin/sh, kernel_init calls panic("No working init found") — misconfigured rootfs/ramdisk bricks boot.
- ptrace hooks in kernel_clone (PTRACE_EVENT_FORK/VFORK/CLONE) expose every process creation to tracers; ptrace_event_enabled checks happen before copy_process, so tracer attachments change observable behavior — a classic audit/evasion concern.
- Correctness depends on out-of-tree validation: no CI config exists in the repo, selftests cover only a slice of core code, and the scheduler/mm concurrency contracts (documented in block comments like the __schedule memory-barrier notes) are enforced by review rather than automated checks. (inferred)
- The Rust/C boundary is a concentrated audit point: rust/kernel/lib.rs wraps C APIs with documented safety invariants and explicitly forbids bypassing the crate; new wrappers that misstate C contracts propagate unsoundness to every Rust driver. (inferred)