Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(p7ll): port setup_signals from ircd.c to Rust

The boot-time signal-disposition installer (ircd.c:1388, called once from
the still-C main). All callees are now Rust (dummy P7a; s_rehash/s_restart/
s_die/s_slave P7kk), so this is a clean leaf. De-static'd + body guarded
under -DPORT_IRCD_SETUP_SIGNALS_P7ll on the ircd_link.o recipe; the cref
archive keeps the de-static'd body for the L1 oracle. Config-resolved:
POSIX_SIGNALS sigaction branch, SIGWINCH defined, USE_IAUTH (s_slave +
SIGCHLD/SA_NOCLDWAIT), not __FreeBSD__, no RESTARTING_SYSTEMCALLS;
faithful cumulative sa_mask.

+136 -3
+74
docs/superpowers/plans/2026-06-07-p7ll-setup_signals.md
··· 1 + # P7ll — `ircd.c` `setup_signals` (signal installer) 2 + 3 + ## Scope 4 + 5 + Port the static boot-time signal installer `setup_signals(void)` (ircd.c:1388) to 6 + `ircd-common/src/ircd.rs`. It is called once from the still-C `main()` (ircd.c:1033), 7 + after config read. P7kk explicitly deferred this to "its own sub-phase" because the 8 + installer references `dummy` (now Rust, P7a) and the four signal handlers 9 + `s_rehash`/`s_restart`/`s_die`/`s_slave` (now Rust, P7kk) — so all callees are finally 10 + Rust and the leaf is portable. 11 + 12 + ## Config-resolved body (locked config) 13 + 14 + `POSIX_SIGNALS = 1` (setup.h:347) → the `sigaction` branch (not the `signal()` else). 15 + `SIGWINCH` defined on Linux → its `sigaddset`+`sigaction` compile. `USE_IAUTH` ON → 16 + the `s_slave` (SIGUSR1) arm + the `SIGCHLD`/`SA_NOCLDWAIT` arm compile. `SA_NOCLDWAIT` 17 + defined (glibc/musl) → `act.sa_flags = SA_NOCLDWAIT` for SIGCHLD. `RESTARTING_SYSTEMCALLS` 18 + undef → no `siginterrupt`. not `__FreeBSD__` → no SIGTRAP arm. 19 + 20 + The compiled installer, in order (cumulative `sa_mask`, `sigemptyset` called twice): 21 + 22 + | signal | handler | sa_flags | sa_mask (cumulative) | 23 + |---------|--------------------|--------------|---------------------------------| 24 + | SIGWINCH| SIG_IGN | 0 | {PIPE,ALRM,WINCH} | 25 + | SIGPIPE | SIG_IGN | 0 | {PIPE,ALRM,WINCH} | 26 + | SIGALRM | dummy | 0 | {PIPE,ALRM,WINCH} | 27 + | (sigemptyset resets mask) | | | | 28 + | SIGHUP | s_rehash | 0 | {HUP} | 29 + | SIGINT | s_restart | 0 | {HUP,INT} | 30 + | SIGTERM | s_die | 0 | {HUP,INT,TERM} | 31 + | SIGUSR1 | s_slave | 0 | {HUP,INT,TERM,USR1} | 32 + | SIGCHLD | SIG_IGN | SA_NOCLDWAIT | {HUP,INT,TERM,USR1,CHLD} | 33 + 34 + ## Seam / guard 35 + 36 + `setup_signals` is `static` → no `cref_` oracle symbol. De-static it under 37 + `#ifndef PORT_IRCD_SETUP_SIGNALS_P7ll` (the P7kk/P7jj precedent), splitting the line-34 38 + combined static proto `static void open_debugfile(void), setup_signals(void), io_loop(void);` 39 + into `open_debugfile`/`io_loop` (stay static) + a guarded `setup_signals` (an `#else 40 + extern void setup_signals(void);` proto so the still-C `main()` keeps its declaration). 41 + Add `-DPORT_IRCD_SETUP_SIGNALS_P7ll` to the `ircd_link.o` recipe in `ircd-sys/build.rs`. 42 + 43 + ## Classification → test strategy 44 + 45 + Boot callee (no `msgtab` entry). Reached once at boot from `main()`. 46 + 47 + - **L1** (`setup_signals_diff.rs`): the installer is differentially testable — it mutates 48 + process *signal dispositions*, not shared memory tables, and those can be 49 + snapshot/restored (P7kk precedent). Save all 8 dispositions; call the Rust 50 + `setup_signals` → query the 8 back; call `cref_setup_signals` → query the 8 back; 51 + restore. Assert, for every signal: `sa_flags` identical between worlds, `sa_mask` 52 + identical (memcmp the `sigset_t`), and the installed handler is the *correct symbol for 53 + that world* — SIG_IGN for SIGPIPE/SIGWINCH/SIGCHLD (identical), `dummy`/`s_rehash`/… 54 + in the Rust world vs `cref_dummy`/`cref_s_rehash`/… in the oracle world. Handler 55 + pointers differ *by design* (Rust vs cref symbol); the equality being verified is 56 + "right handler wired to right signal with the right mask/flags". Serialize on 57 + `GLOBALS_LOCK` (process-global dispositions; the P7 shared-global hazard). The 58 + uninitialized C `sa_restorer` doesn't leak — glibc overrides it during the syscall, so 59 + the queried-back struct is identical in both worlds. 60 + - **L2** (no new file): the still-C `main()` calls `setup_signals` on every `ircd-golden` 61 + boot, installing the Rust handlers — so the entire golden suite is the gate 62 + (`golden_registration` + the P7kk `golden_s_serv_shutdown`/`_maint` rehash/restart/die 63 + paths that read the installed handlers). Byte-identical = no regression. 64 + - **S2S**: none — formats no command-driven remote-user wire fields. 65 + 66 + ## Checklist 67 + 68 + 1. [ ] RED: write `ircd-testkit/tests/setup_signals_diff.rs` (fails to link: undefined `setup_signals`/`cref_setup_signals`). 69 + 2. [ ] Edit `ircd/ircd.c`: split line-34 proto, guard `setup_signals` body under `PORT_IRCD_SETUP_SIGNALS_P7ll`. 70 + 3. [ ] Add `-DPORT_IRCD_SETUP_SIGNALS_P7ll` to `ircd_link.o` recipe in `ircd-sys/build.rs`. 71 + 4. [ ] GREEN: port `setup_signals` to `ircd-common/src/ircd.rs` (`crate::bsd::dummy` for SIGALRM). 72 + 5. [ ] L1 green: `cargo test -p ircd-testkit --test setup_signals_diff`. 73 + 6. [ ] Build (0 warnings) + golden no-regression spot-check. 74 + 7. [ ] Commit (C edit + port + test separately per the one-test-per-commit rule); update PLAN.md + both P7 logs.
+52
ircd-common/src/ircd.rs
··· 960 960 } 961 961 dorestart = 1; 962 962 } 963 + 964 + /// `setup_signals(void)` (ircd.c:1388) — the boot-time signal-disposition installer, 965 + /// called once from the still-C `main()`. Config-resolved to the locked build: 966 + /// `POSIX_SIGNALS`=1 (the `sigaction` branch), `SIGWINCH` defined, `USE_IAUTH` ON 967 + /// (`s_slave` + the `SIGCHLD`/`SA_NOCLDWAIT` arm), `SA_NOCLDWAIT` defined, not 968 + /// `__FreeBSD__`, no `RESTARTING_SYSTEMCALLS`. The `sa_mask` is cumulative 969 + /// (`sigemptyset` is called exactly twice — once at the top, once before SIGHUP). 970 + #[no_mangle] 971 + pub unsafe extern "C" fn setup_signals() { 972 + let mut act: libc::sigaction = std::mem::zeroed(); 973 + 974 + // SIG_IGN'd: SIGPIPE, SIGWINCH (mask carries PIPE/ALRM/WINCH for the IGN+dummy set). 975 + act.sa_sigaction = libc::SIG_IGN; 976 + act.sa_flags = 0; 977 + libc::sigemptyset(&mut act.sa_mask); 978 + libc::sigaddset(&mut act.sa_mask, libc::SIGPIPE); 979 + libc::sigaddset(&mut act.sa_mask, libc::SIGALRM); 980 + libc::sigaddset(&mut act.sa_mask, libc::SIGWINCH); 981 + libc::sigaction(libc::SIGWINCH, &act, std::ptr::null_mut()); 982 + libc::sigaction(libc::SIGPIPE, &act, std::ptr::null_mut()); 983 + 984 + // SIGALRM: the re-arming no-op `dummy` (bsd.rs, P7a). 985 + act.sa_sigaction = crate::bsd::dummy as unsafe extern "C" fn(c_int) as usize; 986 + libc::sigaction(libc::SIGALRM, &act, std::ptr::null_mut()); 987 + 988 + // The four real handlers (P7kk) — mask reset, then accumulated. 989 + act.sa_sigaction = s_rehash as unsafe extern "C" fn(c_int) as usize; 990 + libc::sigemptyset(&mut act.sa_mask); 991 + libc::sigaddset(&mut act.sa_mask, libc::SIGHUP); 992 + libc::sigaction(libc::SIGHUP, &act, std::ptr::null_mut()); 993 + 994 + act.sa_sigaction = s_restart as unsafe extern "C" fn(c_int) as usize; 995 + libc::sigaddset(&mut act.sa_mask, libc::SIGINT); 996 + libc::sigaction(libc::SIGINT, &act, std::ptr::null_mut()); 997 + 998 + act.sa_sigaction = s_die as unsafe extern "C" fn(c_int) as usize; 999 + libc::sigaddset(&mut act.sa_mask, libc::SIGTERM); 1000 + libc::sigaction(libc::SIGTERM, &act, std::ptr::null_mut()); 1001 + 1002 + // USE_IAUTH: SIGUSR1 → s_slave. 1003 + act.sa_sigaction = s_slave as unsafe extern "C" fn(c_int) as usize; 1004 + libc::sigaddset(&mut act.sa_mask, libc::SIGUSR1); 1005 + libc::sigaction(libc::SIGUSR1, &act, std::ptr::null_mut()); 1006 + 1007 + // USE_IAUTH: reap children without zombies (SA_NOCLDWAIT defined). 1008 + act.sa_sigaction = libc::SIG_IGN; 1009 + act.sa_flags = libc::SA_NOCLDWAIT; 1010 + libc::sigaddset(&mut act.sa_mask, libc::SIGCHLD); 1011 + libc::sigaction(libc::SIGCHLD, &act, std::ptr::null_mut()); 1012 + 1013 + // not __FreeBSD__ → no SIGTRAP arm; no RESTARTING_SYSTEMCALLS → no siginterrupt. 1014 + }
+1 -1
ircd-sys/build.rs
··· 232 232 .current_dir(&cbuild) 233 233 .arg(format!("CC={cc_override}")) 234 234 .arg( 235 - "--eval=ircd_link.o: ../ircd/ircd.c\n\t$(CC) $(S_CFLAGS) -DPORT_IRCD_TUNE_P7c -DPORT_IRCD_CALC_PREF_P7t -DPORT_IRCD_TRY_CONNECTIONS_P7gg -DPORT_IRCD_CHECK_PINGS_P7hh -DPORT_IRCD_DELAYED_KILLS_P7ii -DPORT_IRCD_SETUP_ME_P7jj -DPORT_IRCD_SIGNALS_P7kk \ 235 + "--eval=ircd_link.o: ../ircd/ircd.c\n\t$(CC) $(S_CFLAGS) -DPORT_IRCD_TUNE_P7c -DPORT_IRCD_CALC_PREF_P7t -DPORT_IRCD_TRY_CONNECTIONS_P7gg -DPORT_IRCD_CHECK_PINGS_P7hh -DPORT_IRCD_DELAYED_KILLS_P7ii -DPORT_IRCD_SETUP_ME_P7jj -DPORT_IRCD_SIGNALS_P7kk -DPORT_IRCD_SETUP_SIGNALS_P7ll \ 236 236 -DIRCDCONF_PATH=\"\\\"$(IRCDCONF_PATH)\\\"\" \ 237 237 -DIRCDTUNE_PATH=\"\\\"$(IRCDTUNE_PATH)\\\"\" \ 238 238 -DIRCDMOTD_PATH=\"\\\"$(IRCDMOTD_PATH)\\\"\" \
+9 -2
ircd/ircd.c
··· 31 31 aClient me; /* That's me */ 32 32 aClient *client = &me; /* Pointer to beginning of Client list */ 33 33 34 - static void open_debugfile(void), setup_signals(void), io_loop(void); 34 + static void open_debugfile(void), io_loop(void); 35 + /* P7ll: setup_signals de-static'd (was in the line-34 static proto group) so the 36 + ** cref archive's prefix-rename exports cref_setup_signals for the L1 differential; 37 + ** the body is ported to Rust (ircd-common/src/ircd.rs), guarded out of ircd_link.o 38 + ** by -DPORT_IRCD_SETUP_SIGNALS_P7ll. */ 39 + void setup_signals(void); 35 40 36 41 #if defined(USE_IAUTH) 37 42 /* P7kk: de-static'd (was `static`) so the cref archive's prefix-rename exports ··· 1385 1390 return; 1386 1391 } 1387 1392 1388 - static void setup_signals(void) 1393 + #ifndef PORT_IRCD_SETUP_SIGNALS_P7ll /* ported to ircd-common/src/ircd.rs (P7ll) */ 1394 + void setup_signals(void) 1389 1395 { 1390 1396 #if POSIX_SIGNALS 1391 1397 struct sigaction act; ··· 1480 1486 (void)siginterrupt(SIGALRM, 1); 1481 1487 #endif 1482 1488 } 1489 + #endif /* PORT_IRCD_SETUP_SIGNALS_P7ll */ 1483 1490 1484 1491 /* 1485 1492 * Called from bigger_hash_table(), s_die(), server_reboot(),