syscall
3 remembered, 26 forgotten.
syscall-access-handler
forgotten
Native access syscall (angr-k3ol.2, commit 69c79c494) ships in syscalls/file_path.rs::NativeAccessSyscall. Architecture: reads NUL-terminated path via read_path() (256-byte cap, symbolic byte → SyscallError::SymbolicArgument fallback), then queries the new FileSystem::known_paths Arc<HashSet>. Returns 0 if known, NEG_ONE otherwise.\n\nFileSystem::known_paths is populated by open / openat / open_with_content (each call registers its path name), and there's an explicit register_known_path setter for tests / future Python sync. Pre-populated Python state.fs._files keys are NOT mirrored automatically — same trade-off as the FD-allocating handlers (see syscall-vs-procedure-dispatch memory).\n\nSerde: FileSystemData gained a known_paths: BTreeSet field with #[serde(default)] so pre-angr-k3ol.2 snapshots still load (empty set after deserialize matches previous-zero-known-paths default).\n\nPer-arch registration (mod.rs comment block updated to reflect access is now native, not falling back):\n AMD64: 21, X86: 33, ARM: 33, MIPS32: 4033.\n AArch64 has no legacy access — only faccessat (already a stub).\n\nTests: 7 new cargo tests in syscalls::file_path::tests (unknown→-1, known→0, known-after-open→0, registered-without-open→0, empty→-1, symbolic-byte→fallback, symbolic-ptr→fallback, all-arches roundtrip). One Python parametrized test TestNativeAccessSyscall asserts syscall_python_fallback_count == 0.
syscall-arch-number-table-staleness
forgotten
file_path_stubs_registered_on_all_arches in native/angr/src/syscalls/mod_tests.rs is a registration table (arch -> lstat/newfstatat/readlink/readlinkat/faccessat syscall numbers) that must be updated whenever a stat-family syscall number changes per arch. It was left STALE and red by the i386 stat64 commit (bf81b4ff5): that commit moved X86 lstat from legacy 107 to LFS 196 but did not update the table row, so the cargo --lib test was failing on baseline before angr-11djq.5.2 even started. The ARM struct-stat commit (0ef7ab5f7) repaired both the X86 and ARM rows to the LFS numbers lstat64=196 + fstatat64=327 (angr's i386/arm number maps have NO legacy 106/107 entry per i386-struct-stat-and-angr-syscall-map). LESSON: when registering or changing per-arch syscall numbers in syscalls/mod.rs, grep mod_tests.rs for a matching expectation table and update it in the SAME commit. Also: the two pyo3 interpreter_lifecycle panics (fork_preserves_posix_brk, fork_preserves_mmap_base) under cargo --lib are an environmental Python-interpreter-required failure, NOT a regression -- they fail outside a Python-enabled harness regardless of stat changes.
syscall-audit-promote-stub-pattern
forgotten
Syscall stub audit pattern (angr-6009): when a syscall is registered as 'native' but only mirrors syscall_stub.py (fresh symbolic, no buffer/struct fills), the audit must (a) identify which can be promoted using existing FileSystem helpers — easy wins like faccessat = NativeAccessSyscall + dirfd; (b) document deferred wins with their regression risk — readlink* returning -1 may break /proc/self/exe-discovery binaries; (c) file follow-up beads. The 'mirror Python' default loses information the FileSystem model already has. Audit changes: cargo unit tests cover semantics, Python TestNative*Syscall classes cover cross-FFI dispatch (assert syscall_python_fallback_count == 0). When promoting an *at syscall from stub to real, also drop it from the symbolic-return parametrize harness in tests/engines/rust/test_syscalls.py — the harness uses all-zero args which the new handler treats as 'pathname at unmapped addr 0', falling back to Python and breaking the fallback==0 assertion.
syscall-cc-override-needed
forgotten
When adding a new arch's syscall registrations to NativeSyscallRegistry, check that the calling convention's syscall_arg_registers() actually defines the right register window for the kernel ABI. The default impl falls back to arg_registers() (the C ABI), which can be (a) empty (Cdecl x86 was stack-only) or (b) a strict subset (ARMEABI C ABI is R0-R3, but kernel uses R0-R5). Without an override, extract_syscall_args silently zero-pads missing args and the syscall handler receives a fd=0 / count=0 / etc. without ever raising. Override on the CC, not at each handler. AArch64CC (X0-X7) and MipsO32 (-) are wide enough to skip the override. See native/angr/src/arch/calling_conventions.rs::Cdecl::syscall_arg_registers for the i386 reference (commit 33dbc05fa, angr-7xms).
syscall-directory-0hif2-pattern
forgotten
Native directory syscalls (angr-0hif.2, commit 54a32fad9): chdir / fchdir / getcwd back a new FileSystem::cwd: Vec field (default b'/', no normalization, matches procedures/linux_kernel/cwd.py::chdir which also assigns the raw concrete path). mkdir / mkdirat / rmdir / unlink / unlinkat / rename / renameat / renameat2 mirror syscall_stub via ContinueSymbolic — none have a Python SimProcedure except unlink (which needs path → SimFile plumbing not yet in RustSimState, deferred alongside angr-k3ol). Per-arch syscall numbers: AMD64=79/80/81/82/83/84/87/258/263/264/316, X86=10/12/38/39/40/133/183/296/301/302/353, ARM EABI=same legacy as X86 + 323/328/329 (no renameat2 in angr's table), ARM64=17/49/50/34/35/38 (no legacy mkdir/rmdir/unlink/rename in asm-generic, no renameat2), MIPS-O32=4010/4012/4038/4039/4040/4133/4203/4289/4294/4295 (no renameat2). Note: MIPS O32 *at numbers in angr's table differ from upstream Linux (4289 mkdirat vs upstream 4287, etc.); use angr's table values to stay consistent with how angr's SimSyscallLibrary maps names → numbers.
syscall-dup-vp19-pattern
forgotten
Native dup/dup2/dup3 syscall handlers (angr-vp19, commit 3b21cd9f2): pattern follows libc NativeDup precedent (native/angr/src/procedures/fileops.rs:233) — mutate RustSimState::file_system() directly, do NOT sync Python state.posix.fd. The bd issue's blocker description 'needs FD-table plumbed through RustSimState' was outdated; FileSystem is already a field on RustSimState with dup/dup2 methods. Real divergence from Python: Rust uses monotonic next_fd, Python uses lowest-free fd allocation — surfaces only when an exploration closes an fd and expects slot reuse. Per-arch syscall numbers: AMD64=32/33/292, X86=41/63/330, ARM=41/63/358, ARM64=23/-/24 (asm-generic drops legacy dup2), MIPS32=4041/4063/4327. dup2/dup3 enforce 4096-fd ceiling matching procedures/posix/dup.py; -EBADF returned as kernel-ABI negative errno ((-9_i64) as u64). dup3 accepts but ignores O_CLOEXEC flag (close-on-exec unmodeled).
syscall-epic-0hif-closed-pattern
forgotten
Syscall epic angr-0hif (native parity campaign) closed 2026-06-02 — all 7 children done. Current native syscall handler files in native/angr/src/syscalls/: arch_prctl, brk, concurrency, directory, exit, file_descriptor, file_path, identity, memory_extras, mmap, mprotect, munmap, read, rlimit, sigaction, signals, sim_time, write. Native syscall count >40 across AMD64 (~50 registrations), X86 (~50), ARM (~50), ARM64 (~40, asm-generic drops legacy), MIPS-O32 (~50). Pattern that worked across all 7 children: (1) check if syscall has a Python SimProcedure — if not, use stub_syscall! macro to mirror procedures/stubs/syscall_stub.py via ContinueSymbolic; (2) if Python proc has real semantics that touch RustSimState-modeled fields (e.g. file_system, brk, time), implement the semantics natively; (3) if Python proc touches state.posix.fd or state.fs.SimFile path-map (still Python-only), leave as a stub for now and document the trade-off (k3ol follow-up). Per-arch sweep test pattern: per-syscall (label, expected name, arity), table of (arch, num) tuples, asserts handler.name() / num_args() — see file_path::file_path_stubs_registered_on_all_arches as template.
syscall-epic-k3ol-closed-summary
forgotten
angr-k3ol epic CLOSED 2026-06-02 (all 4 children done). Native file-path syscall coverage:
- angr-k3ol.1 (1eb858da2): open / openat / close via FileSystem::open / close.
- angr-k3ol.2 (69c79c494): access via FileSystem::is_path_known.
- angr-k3ol.3 (2dfdd68d3): fstat for AMD64 (5) + ARM64 (80) via FileSystem::fd_info → write_amd64_stat / write_aarch64_stat.
- angr-k3ol.4 (b57f08b22): stat for AMD64 (4) via FileSystem::content_size_for_path + write_amd64_stat. ARM64 has no legacy stat (only newfstatat 79).
Final design choices worth carrying forward:
- Stat does NOT mint a temp fd (diverges from Python stat.py's open→fstat→close). Uses content_size_for_path instead — keeps next_fd stable across stat queries.
- All five symbolic-return relatives (lstat, newfstatat, readlink, readlinkat, faccessat) stay as stubs — neither Python angr nor the Rust handler has logic; both emit fresh symbolic.
- Other arches' legacy 32-bit struct stat path remains Python-error (Python proc raises). Not worth a Rust copy.
- state.posix.fd / state.fs are NOT mirrored back to Python beyond FileSystem::known_paths. Same trade-off as the libc fileops procs.
Surface: syscall_python_fallback_count == 0 for the entire file-path family on AMD64 in fast smoke tests. The remaining file-path-y syscalls (chmod, chown, utime, etc.) are not in scope — track separately if needed.
syscall-fallback-root-cause
remembered
Python syscall fallback (rust_callback_dispatch::_handle_syscall_callback_inner) was 100% broken before angr-89w70, in three compounding ways, all hidden by one 'except Exception' that resumed at addr+1 with no changes: (1) engine.process(state, procedure=None) on factory.default_engine ALWAYS raises TypeError — SimEngineSyscall.process_successors forwards *kwargs into process_procedure which already takes procedure positionally. Use factory.procedure_engine.process(state, procedure=sc_proc) with the handler resolved via simos.syscall(state); the syscall mixin cannot resolve it for us because the Rust-built callback state has no Ijk_Sys parent jumpkind. (2) _extract_memory_changes returns a (concrete, symbolic) 2-tuple — resume_after_syscall takes only the concrete half; unpack, _filter_symbolic_addrs, then import_symbolic_to_state AFTER resume. (3) Linux SYSCALL_CC returns to the 'ip_at_syscall' register (VEX sets it on the syscall exit); Rust's callback state never populates it, so cc.teardown_callsite returns 0 — seed state.regs.ip_at_syscall = state.addr before running the handler. Lesson: a broad except around a bounce hides total breakage; callback_syscall_error_count now counts it.
syscall-fcntl-ioctl-promotion
forgotten
fcntl/fcntl64/ioctl native promotion (angr-aig2, 2026-06-03, commit d74cbcb21). Pattern: keep the existing stub_syscall! macro for pipe/pipe2, replace fcntl/fcntl64/ioctl with hand-written handlers that dispatch on the concrete cmd arg. For cmds we handle (F_GETFD/F_SETFD/F_SETFL → 0, F_GETFL → fd_info().flags.to_posix() or -EBADF, ioctl TIOCGWINSZ → -ENOTTY), return SyscallOutcome::Continue. For unknown cmd, build a fresh symbolic via state.solver().borrow() / RustBV::symbolic and return ContinueSymbolic — symbolic_return() helper centralizes this. Symbolic cmd surfaces SyscallError::SymbolicArgument so the caller falls back to syscall_stub. TIOCGWINSZ const differs by arch: 0x5413 generic (x86/amd64/arm/arm64), 0x40087468 MIPS; arch dispatch via Arch::name() string match. FIONREAD intentionally skipped — would need to write a byte count back into the int* argument, and the syscall-side memory-write helpers are not yet plumbed here.
syscall-fstat-handler
forgotten
Native fstat syscall (angr-k3ol.3, commit 2dfdd68d3) ships in syscalls/file_path.rs::NativeFstatSyscall. Arch coverage: AMD64 (sys 5) + ARM64 (sys 80) only. i386 / ARM EABI / MIPS32 (sys 108/108/4108) use the LEGACY 32-bit struct stat AND have no Python implementation in angr/procedures/linux_kernel/fstat.py either — handler raises SyscallError::Other and dispatcher falls back to Python's SimProcedureError. No reason to write a Rust 32-bit copy.
Architecture: arch check FIRST (avoid mutating state.memory on unsupported), then fd lookup via FileSystem::fd_info(fd).3 = content_len. Per-arch write_amd64_stat (144 bytes, 0x90) and write_aarch64_stat (128 bytes, 0x80) helpers mirror Python's _store_amd64 / _store_aarch64 field offsets EXACTLY. Two intentional divergences from state.posix.fstat_with_result:
- st_mode = concrete S_IFREG | 0o755 (0o100755 = 33261) instead of fresh BVS('st_mode', 32). Python mints a symbolic; we write a concrete that matches a regular file's permissions.
- st_size = concrete content_len (FileSystem already has it) instead of fresh BVS('st_size', 64).
Other fields: st_dev/st_ino/st_nlink/st_uid/st_gid/st_rdev/st_blocks/st_time=0; st_blksize=0x400 (matches Python default). Unknown fd → -1 with no buffer write (matches Python's result=-1 branch).
Tests: 7 cargo tests (unknown-fd, known-fd-amd64, known-fd-aarch64, unsupported-arch, symbolic-fd, symbolic-buf, unmapped-buf-Memory-error). 1 Python integration test TestNativeFstatSyscall::test_fstat_unknown_fd_dispatches_natively pinning syscall_python_fallback_count == 0.
syscall-handlers-arch-agnostic
remembered
Existing native syscall handlers in native/angr/src/syscalls/ (read/write/exit/brk/mprotect/munmap/gettimeofday/time/clock_gettime/rt_sigaction) are arch-AGNOSTIC — they accept RustBV args from the dispatcher and operate on RustSimState. To enable them on a new arch, just add a 'r.register("", <syscall_num>, ...)' line in NativeSyscallRegistry::new. mmap is arch-agnostic too, but only on archs whose syscall ABI matches the modern 6-register byte-offset form (AMD64, ARM64). x86/ARM/MIPS32 use a legacy struct-arg mmap or mmap2-with-page-offset, both of which need a distinct handler — DON'T just register NativeMmapSyscall under those arches' mmap2 numbers. arch_prctl is amd64-only.
syscall-lstat-newfstatat-promotion
forgotten
angr-poao (2026-06-03, commit 4de707b75) promoted lstat (AMD64 sycall 6) and newfstatat (262 AMD64, 79 ARM64) from stub-symbolic to stat-shaped handlers in syscalls/file_path.rs. lstat collapses to stat semantics (FileSystem has no symlinks) — AMD64 only. newfstatat clones stat + openat-style dirfd handling (absolute/AT_FDCWD via FileSystem, relative+non-AT_FDCWD → -1, AT_EMPTY_PATH ignored / deferred) — AMD64 + ARM64 (newfstatat is the only stat-shaped syscall on ARM64 asm-generic). Both reuse write_amd64_stat / write_aarch64_stat from NativeFstatSyscall + content_size_for_path from NativeStatSyscall. Arch check FIRST before any memory read. Tests: 16 new cargo (lstat 6 + newfstatat 10) + Python TestNativeLstatSyscall/TestNativeNewfstatatSyscall — pattern: drop entry from TestNativeFilePathSyscalls parametrize (all-zero-arg harness no longer fits, same precedent as faccessat angr-6009) AND from stub_handlers_return_fresh_symbolic_on_all_arches cargo sweep. Followup angr-wv38 (readlink/readlinkat → -1) still open.
syscall-mmap-family-i386-arm-mips32
forgotten
Native old_mmap/mmap2 handlers (angr-6gmc, 2026-06-02): legacy struct-arg mmap and page-offset mmap2 now have native Rust handlers, sharing a 'do_mmap' core extracted from the existing modern mmap handler. Coverage: i386 (90 old_mmap, 192 mmap2), ARM EABI (90 old_mmap, 192 mmap2), MIPS32 O32 (4090 old_mmap only). MIPS32 mmap2 (4210) still falls back to Python because O32 ABI passes args 5-6 on the stack and extract_syscall_args does not currently traverse it. old_mmap reads six 32-bit fields from memory using state.memory_load (which respects state endness, so works on BE arches once we add BE variants). mmap2 scales the page-offset arg by PAGE_SIZE before dispatch — matches procedures/linux_kernel/mmap.py::mmap2's explicit zero-extend + multiply. Test pattern: 14 new cargo tests in syscalls::mmap::tests cover struct reading (dispatch_anonymous_struct_call), bad-flags (-1 return), file-backed fallback, symbolic-field SymbolicArgument error, offset scaling, and handler metadata. Per-arch registry tests were updated to flip from .is_none() to .is_some() for the now-registered slots and to keep the MIPS32 4210 is_none() assertion.
syscall-native-count-stat
forgotten
Rust engine exposes syscall_native_count + syscall_native_by_num in mgr.stats (symmetric to syscall_python_fallback_count/_by_num). Incremented in exploration/stepping.rs ONLY when a NativeSyscall handler.call returns Ok (Err falls through to Python callback and counts as fallback). Emitted from both PerfStats dict sites in exploration/stats_api.rs. Use these to assert native syscall dispatch fired in tests (e.g. TestCgcEndToEndNativeDispatch for CGC). There is NO per-handler-name counter, only by syscall number.
syscall-numbers-fd-control
forgotten
FD control syscalls (fcntl/fcntl64/ioctl/pipe/pipe2) registered in native/angr/src/syscalls/file_descriptor.rs via angr-0hif.5 (commit 3d1273c4b, 2026-06-01). Numbers by arch:
- fcntl: AMD64=72, X86=55, ARM=55, ARM64=25, MIPS32=4055
- fcntl64 (32-bit only — asm-generic ABI absorbed it into fcntl): X86=221, ARM=221, MIPS32=4220
- ioctl: AMD64=16, X86=54, ARM=54, ARM64=29, MIPS32=4054
- pipe (ARM64 absent — asm-generic only has pipe2): AMD64=22, X86=42, ARM=42, MIPS32=4042
- pipe2: AMD64=293, X86=331, ARM=359, ARM64=59, MIPS32=4328
All 5 are pure stub-fallthrough — posix/fcntl.py defines a fcntl class but linux_kernel.py does NOT bind it into the SimSyscallLibrary (only dup is wired from posix/ via lib.add("dup", ...)), so the syscall path hits syscall_stub.py rather than the libc proc. The native handlers mirror that with RustBV::symbolic(width=arch().bits()) via SyscallOutcome::ContinueSymbolic.
dup/dup2/dup3 NOT registered — they have posix/dup.py procs that mutate state.posix.fd and need the FD table plumbed through RustSimState. Same blocker as angr-k3ol; tracked under angr-vp19 (P3).
syscall-numbers-file-path
forgotten
Linux syscall numbers for file-path family (angr-0hif.1, file_path.rs registry):
- AMD64: lstat=6, readlink=89, newfstatat=262, readlinkat=267, faccessat=269
- X86: readlink=85, lstat=107, readlinkat=305, faccessat=307 (newfstatat absent — uses fstatat64=327)
- ARM (EABI): readlink=85, lstat=107, readlinkat=332, faccessat=334 (newfstatat absent — uses fstatat64=327)
- ARM64 (asm-generic): faccessat=48, readlinkat=78, newfstatat=79 (NO lstat, NO readlink — only *at variants)
- MIPS32 O32: readlink=4085, lstat=4107, readlinkat=4298, faccessat=4300 (newfstatat absent — uses fstatat64=4293)
These 5 syscalls have no Python SimProcedure (linux_kernel/ has stat.py / fstat.py / access.py but not lstat / newfstatat / readlink / readlinkat / faccessat). Falls through to procedures/stubs/syscall_stub.py::syscall which returns Unconstrained. Native handlers in syscalls/file_path.rs mirror via SyscallOutcome::ContinueSymbolic.
The remaining file-path syscalls (open/openat/close/stat/fstat/access) all HAVE Python procs that touch state.posix.fd / state.fs and need FD-table / SimFile plumbing into RustSimState. Tracked under follow-up bead angr-k3ol.
syscall-numbers-memory-extras
forgotten
Linux syscall numbers for memory-advisory ops (madvise/mremap/msync/mlock/munlock/mlockall/munlockall) by arch in angr's linux_kernel.py number table:
- AMD64 (unistd_64.h): madvise=28, mremap=25, msync=26, mlock=149, munlock=150, mlockall=151, munlockall=152.
- X86 (unistd_32.h): madvise=219, mremap=163, msync=144, mlock=150, munlock=151, mlockall=152, munlockall=153.
- ARM EABI: madvise=220, mremap=163, msync=144, mlock=150, munlock=151, mlockall=152, munlockall=153 (note madvise=220 ≠ X86's 219).
- ARM64 (asm-generic): madvise=233, mremap=216, msync=227, mlock=228, munlock=229, mlockall=230, munlockall=231.
- MIPS32 O32 (offsets +4000): madvise=4218, mremap=4167, msync=4144, mlock=4154, munlock=4155, mlockall=4156, munlockall=4157. Source: angr/procedures/definitions/linux_kernel.py add_number_mapping_from_dict tables (search for the syscall name).
syscall-numbers-signals
forgotten
Linux syscall numbers for signals + process control (angr-0hif.6, signals.rs registry):
- AMD64: kill=62, tgkill=234, rt_sigreturn=15, pause=34, alarm=37, rt_sigprocmask=14 (deferred)
- X86: kill=37, tgkill=270, rt_sigreturn=173, pause=29, alarm=27, rt_sigprocmask=175
- ARM (EABI): kill=37, tgkill=268, rt_sigreturn=173, pause=29, alarm=27, rt_sigprocmask=175
- ARM64 (asm-generic): kill=129, tgkill=131, rt_sigreturn=139 — NO pause, NO alarm (glibc emulates via setitimer + rt_sigtimedwait)
- MIPS32 O32: kill=4037, tgkill=4266, rt_sigreturn=4193, pause=4029, alarm=4027
rt_sigprocmask intentionally NOT registered native — its Python impl in procedures/linux_kernel/sigprocmask.py mutates state.posix.sigmask which RustSimState lacks. Falls back to Python for parity. Same reasoning applies to any other syscall that touches state.posix.* fields not yet plumbed into Rust state.
syscall-open-close-handlers
forgotten
Native open/openat/close syscall handlers (syscalls/file_path.rs, angr-k3ol.1, commit 1eb858da2) operate on RustSimState::file_system() identically to procedures/fileops::NativeOpen/NativeClose. Pathname read by read_path() helper: NUL-terminated, max 256 bytes; first symbolic byte triggers SymbolicArgument fallback. AT_FDCWD = 0xFFFFFFFFFFFFFF9C (i.e. (u64)(u32)-100 = 4294967196) on amd64 — Python's openat.py also matches this exact unsigned value. Registered: amd64 (2/257/3), x86 (5/295/6), ARM EABI (5/322/6), AArch64 asm-generic (56/57; no legacy open), MIPS32 O32 (4005/4288/4006). Python state.posix.fd / state.fs NOT mirrored — same trade-off as dup/dup2 + libc procs. The companion stat/fstat/access syscalls still fall back to Python; need per-arch struct stat layouts and state.fs mirroring (separate subtasks under angr-k3ol).
syscall-readlink-promotion
forgotten
angr-wv38 (2026-06-03, commit 6505efb91) closed the angr-6009 syscall audit by promoting readlink (AMD64 89, X86 85, ARM 85, MIPS32 4085, MIPS64 5087) and readlinkat (AMD64 267, X86 305, ARM 332, ARM64 78, MIPS32 4298, MIPS64 5257) from stub-symbolic to blanket -1. Rust FileSystem has no symlinks, so every path is -1 (EINVAL for known, ENOENT for unknown), buffer untouched. readlinkat still reads dirfd + applies openat-style branch for shape symmetry with faccessat/openat (falls back on symbolic dirfd) — result is -1 either way. Bench sweep clean: no angr-examples binary depended on a positive readlink return. The stub_handlers_return_fresh_symbolic_on_all_arches cargo sweep test was DELETED (every file_path stub now promoted: faccessat angr-6009, lstat/newfstatat angr-poao, readlink/readlinkat angr-wv38). Cargo file_path tests: 59 → 73 (+14). Pattern: when ALL stubs in a module get promoted, delete the whole sweep test rather than reducing it to an empty list.
syscall-readwrite-full-fdtable
forgotten
Native read/write FD-table generalization (angr-8j16, commit 5fbc987bb): NativeRead/NativeWrite (procedure + syscall) accept any fd that is open in state.file_system_ref(). Stdin (fd=0) keeps the symbolic-bytes path. Non-stdin reads serve concrete content from FileSystem::read; if content_len==0 (open but unwritten) they fall back so Python's symbolic-file model owns the read. write rejects fd=0 (stdin write → Python EBADF) and any fd not open in Rust's FS. Sync invariant: Rust owns fds it created via NativeOpen/NativePipe/NativeDup/NativeFopen + stdio. Python owns fds it created (lowest-free vs Rust monotonic counter mismatch is intentional and remains pending angr-6zxx).
syscall-registry-keyed-lookup
forgotten
NativeSyscallRegistry (native/angr/src/syscalls/mod.rs) keys handlers by arch then num as HashMap<&'static str, HashMap<u64, Arc>>. get(arch:&str,num) does two O(1) hash lookups: the runtime &str arch resolves against the &'static str outer keys because &'static str: Borrow (std blanket impl for &T), then a second get(&num). This replaced a former iter().find() linear scan over all 432 rows that ran once per executed syscall (angr-k95c). Do NOT flatten back to a (&'static str,u64) tuple key: a runtime &str cannot construct/borrow such a tuple key without interning, which is what forced the linear scan originally.
syscall-rlimit-concurrency-native
forgotten
Native syscall handlers for resource-limit + concurrency added in angr-0hif.7 (commit 125958419, 2026-06-01). Files: native/angr/src/syscalls/rlimit.rs (getrlimit/setrlimit/prlimit64), native/angr/src/syscalls/concurrency.rs (futex/eventfd/eventfd2/epoll_create/epoll_create1/epoll_ctl/epoll_wait). getrlimit branches on RLIMIT_STACK (3): concrete 8388608 write at *rlim, fresh symbolic at *rlim+8, return 0; other resources return fresh symbolic. futex op&1 (FUTEX_WAKE) → concrete 0, else fresh symbolic — matches procedures/linux_kernel/futex.py. Stub family uses local stub_syscall! macro (third copy after memory_extras.rs and identity.rs stub_syscall_1arg!; dedup tracked as follow-up). All 5 arches registered with per-arch tests in syscalls::mod::tests::{rlimit_registered_on_all_arches, concurrency_registered_on_all_arches}. ARM64 lacks legacy epoll_create/wait/eventfd — asm-generic drops them. epoll_pwait scoped out by bd description.
syscall-stat-handler
forgotten
Native stat syscall (angr-k3ol.4, commit b57f08b22) ships in syscalls/file_path.rs::NativeStatSyscall. AMD64 only — ARM64 asm-generic ABI dropped legacy stat (only newfstatat 79, already a stub). x86/armel/mipsel carry the legacy 32-bit struct stat with no Python proc, so handler raises Other('unsupported arch ...') and dispatcher falls back to Python's error path. Arch check is FIRST (before reading pathname memory).
Algorithm: resolve pathname via read_path → empty → -1 → not is_path_known → -1 → else lookup size via new FileSystem::content_size_for_path(path) (largest content_len across any fd with matching name, None if only register_known_path) → write_amd64_stat with that size → return 0. Reuses the existing write_amd64_stat helper from angr-k3ol.3. Never mutates the fd table (diverges from Python's stat.py open→fstat→close pattern; next_fd stays stable).
content_size_for_path(name) -> Option lives on FileSystem alongside fd_info — iterates fds.values() filtered by name and takes max(). Public, used by NativeStatSyscall and stable for future tests.
Tests: 9 cargo tests covering unknown / empty / known-with-content / registered-only / largest-content-len / unsupported-arch (4 arches) / symbolic-pathname / symbolic-statbuf / unmapped-statbuf. 1 Python integration test TestNativeStatSyscall::test_stat_unknown_path_dispatches_natively pinning syscall_python_fallback_count == 0. After landing: cargo test syscalls::file_path → 35/35; pytest test_rust_exploration.py → 683/683 (+1 over baseline); regression gate 16/16 in 21.5s.
syscall-stub-fresh-symbolic-pattern
forgotten
Pattern for native syscall handlers with no Python SimProcedure (e.g. setuid/setgid in angr-pqgu): use SyscallOutcome::ContinueSymbolic with a fresh RustBV::symbolic sized to arch().bits() (matches C long width). Symbol name should mirror Python's syscall_stub naming: "syscall_stub_<display_name>". The stub_syscall_1arg! macro in native/angr/src/syscalls/identity.rs collapses the boilerplate. The dispatcher writes the BV directly to the return register via set_register_by_offset; the bit width is derived from arch().bits() on every supported arch (AMD64/X86/ARM/ARM64/MIPS32). Distinct invocations must yield distinct fresh symbols — verifiable by RustBV::Symbolic { id, .. } inequality.
syscall-stub-macro-consolidated
forgotten
stub_syscall! macro (was septuplicated across syscalls/*.rs) was consolidated into native/angr/src/syscalls/mod.rs at pub(crate) scope (commit a510ddaf2, angr-j2ni, 2026-06-01). Net -215 LoC. Key pattern: the shared macro uses fully-qualified $crate:: paths ($crate::syscalls::NativeSyscall, $crate::symbolic::RustBV, $crate::state::RustSimState) so each caller only needs 'use super::stub_syscall;' — no need to also import the trait/types into the parent scope. The per-module imports for NativeSyscall/SyscallOutcome/RustSimState/RustBV that used to live at module scope are now scoped under #[cfg(test)] inside each tests submodule (they're only referenced by tests now). Future syscall stubs: just 'use super::stub_syscall;' then 'stub_syscall!(NativeFooSyscall, "foo", "syscall_stub_foo", N);'.
syscall-stub-macro-generalized
forgotten
Generic 'stub_syscall!' macro pattern (native/angr/src/syscalls/memory_extras.rs, 2026-06-01) generalises identity.rs's stub_syscall_1arg! to arbitrary arity. Params: $ty (struct), $label (display name str), $sym_name (symbol-name str), $nargs (u-literal). Body returns ContinueSymbolic with RustBV::symbolic of width arch().bits(). Use for any syscall with no Python SimProcedure (falls through to syscall_stub.py::syscall) — covers angr-0hif.4 mremap/mlock/munlock/mlockall/munlockall/msync/madvise. identity.rs still has its own stub_syscall_1arg! for setuid/setgid; the two could be merged in a follow-up dedup but each module owning its style is fine for now.
syscall-vs-procedure-dispatch
remembered
Native syscall handlers (native/angr/src/syscalls/) and native libc procedures (native/angr/src/procedures/) live in two separate registries — they are NOT a single dispatch table. NativeRead/NativeWrite procedures (procedures/{read,write}.rs) are picked up when libc 'read()' / 'write()' is called as a SimProcedure. NativeReadSyscall/NativeWriteSyscall (syscalls/{read,write}.rs, added 2026-05-11 angr-0z34) are picked up when the binary issues a raw 'syscall' instruction (#0 / #1 on amd64). Most CTF benchmarks use libc, so most users never exercise the syscall path; statically-linked / Go / hand-rolled-asm binaries do. The two implementations must be kept logically in sync — both use stdin (fd=0) for read and stdout/stderr for write. They share NO code currently, on purpose: SyscallOutcome != Option and the trait shapes differ. If you change one, also change the other.