- name
- linux-internals-dev
- description
- Auth/lab dev: Linux internals; ELF loading and program startup, procfs, namespaces/caps, eBPF verifier/maps, LSM hooks for tooling/telemetry design.
- license
- MIT
- compatibility
- Linux kernels 5.4 through 6.x on x86-64 and ARM64; Interfaces and struct layouts can vary by kernel version and distro patches, so verify against running kernel docs and headers before production use.
- metadata
- {"author":"AeonDave","version":"1.1"}
# linux-internals
Deep Linux internals for practitioners who need to reason below libc and framework abstractions.
This skill is for structural mechanics: what the kernel and loader actually do, what invariants hold, what breaks across versions, and where detection surfaces appear.
---
## When to activate
- You are writing or reviewing custom ELF loaders, in-memory execution, or reflective mapping logic
- You are diagnosing execution before `main` or checking an old startup diagram against the target libc and toolchain
- You are parsing `/proc` for process, memory, mount, or namespace intelligence and need race-safe interpretation
- You are building namespace or container boundary logic with `clone`, `unshare`, `setns`, or pidfd flows
- You are loading eBPF programs and debugging verifier failures, helper constraints, map lifetime, or attach semantics
- You are reasoning about LSM policy behavior, hook order, and process security attribute surfaces
- You need to explain detection and hardening implications of low-level Linux behavior
If the task is ordinary userland API usage, this skill is overkill. If the task is ABI and kernel boundary mechanics, use this skill first.
---
## Territory map
| Domain | File | Covers |
|---|---|---|
| ELF and dynamic linking | `references/elf-format.md` | Load when parsing ELF or tracing interpreter entry, executable entry, libc startup, initializers, and termination |
| procfs semantics | `references/procfs.md` | `/proc` layout, ptrace-gated visibility, `maps` and `smaps` races, `hidepid`, mountinfo and pidns interactions |
| Namespaces and isolation | `references/namespaces.md` | namespace APIs, ownership and capability checks, pid and user namespace rules, lifetime pinning |
| Syscall dispatch internals | `references/syscall-dispatch-linux.md` | x86-64/aarch64 syscall ABI, vDSO gadget extraction (mseal/getrandom changes), dispatch modes (indirect/direct/seccomp-notify/memfd), hooking landscape (LD_PRELOAD/seccomp/eBPF/io_uring with per-version LSM timeline), eBPF rootkit variants (LinkPro/VoidLink/SPiCa/BPFDoor), .eh_frame stack spoofing (open field), kernel hardening per-version table, Landlock progression |
| eBPF architecture | `references/ebpf.md` | `bpf()` command families, map and program lifecycle, verifier model, helper constraints, attach and pin patterns |
| LSM model | `references/lsm.md` | LSM stacking and ordering, active module discovery, process attr interfaces, hook surfaces and development implications |
---
## Quick invariants
| Area | Invariant |
|---|---|
| ELF | Runtime loading is driven by **program headers**, not section headers |
| ELF | `PT_LOAD`: `p_filesz <= p_memsz`; trailing bytes are zero-filled in memory |
| procfs | Access to many `/proc/pid/*` files for other processes is ptrace capability gated |
| procfs | Reading `maps` and `smaps` is inherently racy except single-read snapshots |
| namespaces | Joining namespaces requires capability checks in both current and owning user namespaces |
| pid namespaces | A process cannot move to an ancestor PID namespace |
| syscalls (x86-64) | Arg4 is in `r10`, not `rcx`; `syscall` clobbers `rcx` and `r11` |
| eBPF | Verifier must prove termination, type safety, bounds safety, and reference release on all paths |
| eBPF | Helper calls can invalidate packet pointer proofs; bounds checks often must be redone |
| LSM | Active LSM order matters; checks run in configured order, capability module first |
| LSM | `/sys/kernel/security/lsm` reflects the active stack and check order |
---
## Decision flow: choose the right subsystem lens
1. Issue is binary load or symbol resolution behavior
- Start in `elf-format.md`
- Validate `PT_INTERP`, `DT_NEEDED`, `DT_RPATH` and `DT_RUNPATH`, then relocation type flow
2. Issue is process or memory inspection mismatch
- Start in `procfs.md`
- Validate ptrace permission model, `hidepid` mode, and map-read race conditions
3. Issue is container boundary or namespace transition
- Start in `namespaces.md`
- Validate ownership user namespace, capability context, and one-way PID namespace movement
4. Issue is syscall origin, anti-hooking, or low-level ABI behavior
- Start in `syscall-dispatch-linux.md`
- Validate calling convention (`r10`/`rcx`), dispatch source (vDSO vs libc vs direct), and fallback implications
5. Issue is eBPF load rejection or runtime attach surprises
- Start in `ebpf.md`
- Read verifier error class first, then map/program refs and attach target constraints
6. Issue is policy enforcement or security hook behavior
- Start in `lsm.md`
- Confirm active stack and hook order before attributing allow or deny outcomes
---
## Detection and hardening surface checklist
Use this whenever implementing a low-level technique.
- ELF path
- Does behavior depend on dynamic linker search path state such as RUNPATH, cache, preload, or secure-exec stripping?
- procfs path
- Are you assuming stable snapshots from files documented as racy?
- Are you accidentally leaking sensitive args or env due to permissive proc mount options?
- namespace path
- Are capability checks evaluated in the expected owning user namespace?
- Could namespace lifetime be pinned by open descriptors or bind mounts?
- syscall path
- Is the syscall instruction sourced from the location you think it is (vDSO/libc/direct asm), and does that match your telemetry assumptions?
- Are x86-64 register conventions respected for 4-6 argument syscalls?
- eBPF path
- Are helper or map choices constrained by program type and privilege model?
- Are verifier assumptions broken by helper-induced packet buffer changes?
- LSM path
- Is behavior due to stacked module ordering rather than a single policy engine?
- Are process attrs read from module-specific paths versus legacy aggregate paths?
---
## Pitfalls
- Treating section headers as mandatory at runtime for execution behavior
- Assuming `DT_RPATH` and `DT_RUNPATH` are equivalent in dependency traversal
- Assuming `/proc/pid/maps` partial reads are stable under concurrent VMA changes
- Forgetting `hidepid` and ptrace gating when process inventory appears incomplete
- Expecting `setns` to move the caller itself into a new PID namespace instead of affecting future children
- Trying to reenter same user namespace to regain dropped capabilities
- Passing syscall arg4 in `rcx` instead of `r10` on x86-64 and then debugging phantom failures
- Assuming all direct-syscall approaches have the same observable syscall origin in kernel telemetry
- Ignoring eBPF reference-release paths after socket lookup helpers
- Debugging eBPF without verifier logs, then guessing blindly
- Attributing policy outcomes to one LSM without checking active stack order
---
## Resources
- `references/elf-format.md` for loader/relocation mechanics and a reproducible pre-`main` startup trace
- `references/procfs.md` for process and memory introspection semantics
- `references/namespaces.md` for isolation model and transition constraints
- `references/syscall-dispatch-linux.md` for syscall ABI, dispatch-source tradeoffs, hooking landscape (LD_PRELOAD / seccomp / eBPF / io_uring), vDSO gadget extraction, .eh_frame comparison with Windows .pdata, and fallback design
- `references/ebpf.md` for verifier and syscall-level eBPF operations
- `references/lsm.md` for LSM architecture and policy interaction model
- `scripts/vdso_gadget_extract.py` for enumerating syscall gadgets from vDSO and libc on a running Linux system (Linux only)
View on GitHub