|
| 1 | +# SOW-0036 - Linux SHM futex timeout ABI portability |
| 2 | + |
| 3 | +## Status |
| 4 | + |
| 5 | +Status: completed |
| 6 | + |
| 7 | +Sub-state: source repair, local ABI validation, documentation and CI configuration completed; no downstream deployment. |
| 8 | + |
| 9 | +## Requirements |
| 10 | + |
| 11 | +### Purpose |
| 12 | + |
| 13 | +Preserve real blocking timeouts for idle SHM sessions on 32-bit time64 libc builds. |
| 14 | + |
| 15 | +### User Request |
| 16 | + |
| 17 | +The user supplied a corrected investigation identifying a libc/kernel timespec ABI |
| 18 | +mismatch in the C futex wrapper, with immediate timeouts and idle session CPU use. |
| 19 | +Treat this as an upstream repair; deployment and host configuration are outside scope. |
| 20 | + |
| 21 | +### Assistant Understanding |
| 22 | + |
| 23 | +Facts: C and Rust pass libc timespec pointers to SYS_futex. The receive timeout is |
| 24 | +an unsigned 32-bit millisecond duration. Go uses syscall.Timespec but hardcodes |
| 25 | +64-bit field assignments. Existing Rust timeout tests assert the error only. |
| 26 | + |
| 27 | +Inferences: explicit kernel ABI marshalling repairs the C and Rust failure without |
| 28 | +changing the shared-memory layout or service polling policy. |
| 29 | + |
| 30 | +Unknowns: affected deployed binaries have not been independently traced in this |
| 31 | +session; no production CPU reduction will be claimed from local tests. |
| 32 | + |
| 33 | +### Acceptance Criteria |
| 34 | + |
| 35 | +- Empty SHM receives actually wait for subsecond and multisecond budgets. |
| 36 | +- Finite and infinite receives still wake when a peer sends. |
| 37 | +- ARM time64 C regression fails before the fix and passes after it. |
| 38 | +- Native C/Rust/Go SHM tests and cross-language interop remain passing. |
| 39 | +- ABI policy and reproducible portability checks are documented. |
| 40 | + |
| 41 | +## Analysis |
| 42 | + |
| 43 | +Sources checked: docs/level1-posix-shm.md, docs/code-organization.md, C/Rust/Go SHM |
| 44 | +sources, C/Rust tests, CMakeLists.txt, runtime-safety workflow, SOW-0006 and SOW-0016. |
| 45 | +Pending/current SOWs concern scale, maintainability, downstream integration, platform |
| 46 | +build gating, permissions, and Rust style; all current SOWs are paused. This is a |
| 47 | +latent initial-implementation defect (C wrapper originates in f71db33), not a reversal |
| 48 | +of a completed SOW's specific time64 fix. No SOW claims prior 32-bit time64 validation. |
| 49 | +The local SOW specs directory contains only .gitkeep. The only runtime project skill |
| 50 | +is downstream vendoring preflight; it does not apply to source-only repairs. |
| 51 | + |
| 52 | +Go 386 build inspection also found pre-existing UDS and test compilation errors; |
| 53 | +track those separately rather than expanding this repair into a platform port. |
| 54 | + |
| 55 | +## Pre-Implementation Gate |
| 56 | + |
| 57 | +Status: ready |
| 58 | + |
| 59 | +Problem / root-cause model: |
| 60 | + |
| 61 | +- On 32-bit time64 libc, libc timespec seconds occupy eight bytes, while the |
| 62 | + legacy futex ABI consumes kernel-sized seconds and nanoseconds. A subsecond |
| 63 | + timeout can therefore become zero. Rust shares this pointer-layout assumption. |
| 64 | + |
| 65 | +Evidence reviewed: |
| 66 | + |
| 67 | +- Source wrappers and callers, existing timeout tests, public SHM synchronization |
| 68 | + contract, local Linux UAPI types, and musl's __timedwait.c web source. |
| 69 | +- User live observations are reported evidence, not independently reproduced here. |
| 70 | + |
| 71 | +Affected contracts and surfaces: |
| 72 | + |
| 73 | +- Linux C and Rust SHM waiting, Go relative-time conversion, regression fixtures, |
| 74 | + portability test script, runtime CI, public SHM docs and integrator guide. |
| 75 | +- Public APIs, wire layout, service polling intervals and Windows remain unchanged. |
| 76 | + |
| 77 | +Existing patterns to reuse: |
| 78 | + |
| 79 | +- Preserve monotonic deadlines, EINTR/EAGAIN retries, shared FUTEX_WAIT/WAKE, |
| 80 | + low-priority test runner and existing SHM interop fixtures. |
| 81 | + |
| 82 | +Risk and blast radius: |
| 83 | + |
| 84 | +- Wrong ABI selection can busy-loop or hang all Linux SHM users. Keep legacy |
| 85 | + syscall support for older kernels; validate timeout and wake paths. The maximum |
| 86 | + relative timeout is UINT32_MAX milliseconds, whose seconds fit signed 32 bits, |
| 87 | + so legacy futex marshalling suffices wherever that syscall exists. |
| 88 | + |
| 89 | +Sensitive data handling plan: |
| 90 | + |
| 91 | +- Do not persist hostnames, host paths, private endpoints, personal data or raw |
| 92 | + investigation notes in SOWs, specs, docs, skills, agent instructions or comments. |
| 93 | + Record only source references, synthetic test paths and sanitized measurements. |
| 94 | + |
| 95 | +Implementation plan: |
| 96 | + |
| 97 | +1. Add public receive timing/wake regression tests and reproduce on ARM time64. |
| 98 | +2. Marshal kernel timeout fields in C and Rust; correct Go duration construction. |
| 99 | +3. Add repeatable 32-bit regression command and CI coverage; update public guidance. |
| 100 | +4. Run focused native, cross-ABI and interoperability validation, review changes, |
| 101 | + and commit implementation and completed SOW together. |
| 102 | + |
| 103 | +Validation plan: |
| 104 | + |
| 105 | +- Real ARM Linux user ABI execution under QEMU with musl time64, plus native tests. |
| 106 | +- Short and >1-second idle waits, delayed message wake, zero timeout wake, |
| 107 | + CPU-versus-wall waiting evidence, maximum API timeout interrupted by a message. |
| 108 | +- Rust stable and MSRV 1.91.0; C/Rust/Go SHM and service SHM interop. |
| 109 | +- Search raw timed syscalls, review ABI/endian/null handling, diff check, SOW audit. |
| 110 | + |
| 111 | +Artifact impact plan: |
| 112 | + |
| 113 | +- AGENTS.md: no new responsibility or global workflow. |
| 114 | +- Runtime project skills: source-only repair does not change downstream preflight. |
| 115 | +- Specs: clarify ABI invariant in authoritative docs; avoid duplicate SOW spec. |
| 116 | +- End-user/operator docs: describe portability check and its validation limits. |
| 117 | +- End-user/operator skills: add 32-bit validation guidance to integrator guide. |
| 118 | +- SOW lifecycle: new SOW for latent initial defect; track separate Go port issues. |
| 119 | + |
| 120 | +Open-source reference evidence: |
| 121 | + |
| 122 | +- Web reference: https://git.musl-libc.org/cgit/musl/tree/src/thread/__timedwait.c |
| 123 | + (blob 666093be98516a1c84b2997f075a8dbfcb797b2c), explicitly marshals futex timeouts. |
| 124 | + No external mirrored/cloned repository was used. |
| 125 | + |
| 126 | +Open decisions: |
| 127 | + |
| 128 | +- No product decision is needed: restore existing blocking semantics and retain |
| 129 | + older-kernel compatibility. Do not disable cgroups or deploy changes. |
| 130 | + |
| 131 | +## Implications And Decisions |
| 132 | + |
| 133 | +Use the legacy futex syscall when available with its kernel layout, since every |
| 134 | +supported relative timeout fits; time64-only targets require a two-int64 layout. |
| 135 | +This avoids depending on new kernel support merely because libc uses time64. |
| 136 | + |
| 137 | +## Plan |
| 138 | + |
| 139 | +Follow the four gate implementation steps above, keeping other SOWs paused. |
| 140 | + |
| 141 | +## Execution Log |
| 142 | + |
| 143 | +### 2026-09-27 |
| 144 | + |
| 145 | +- Confirmed the unsafe libc pointer handoff in C and Rust. |
| 146 | +- Confirmed Go 386 build failures in SHM duration construction, UDS Iovlen/sendmsg, |
| 147 | + and oversized test literals. Broader UDS portability is tracked in SOW-0037. |
| 148 | +- Reproduced C immediate expiry on ARM musl and i386 glibc time64. Native x86_64 |
| 149 | + and i386 time32 pass the same fixture before the repair. |
| 150 | +- Added explicit kernel-word marshalling in C and Rust, preserving null infinite |
| 151 | + waits and legacy syscall use for representable relative durations. |
| 152 | +- Rust time64 builds exposed private timespec padding: use Default initialization |
| 153 | + in transport and the benchmark clock helper. The benchmark helper is built by |
| 154 | + Cargo integration tests; no benchmark methodology or performance claim changed. |
| 155 | +- Reproduced the old Rust wrapper under ARM musl time64 in a temporary standalone |
| 156 | + crate with only the timespec initialization compatibility adjustments retained: |
| 157 | + 100 ms returned in 0.894 ms. Repaired public-API test passes time32 and time64. |
| 158 | +- Added C native CTest fixture, C cross-ABI script, Rust public-API integration |
| 159 | + fixture and cross-ABI script, and Runtime Safety ARM CI job. |
| 160 | +- User asked about libc wrappers and language scope. Confirmed that syscall does |
| 161 | + not marshal timespec, musl helpers are internal, C/Rust share the issue, and |
| 162 | + pure Go's syscall.Timespec does not have this libc/kernel mismatch. |
| 163 | +- Full Rust ARM unit tests exposed a separate pthread_t Send test compilation |
| 164 | + failure; tracked in SOW-0037. The standalone SHM public-API fixture runs without |
| 165 | + that unrelated unit-test dependency. |
| 166 | + |
| 167 | +## Validation |
| 168 | + |
| 169 | +Acceptance criteria evidence: |
| 170 | + |
| 171 | +- C ARM musl time64 before: 100 ms returned in 0.278 ms; 1100 ms returned in |
| 172 | + 1000.168 ms; delayed finite receive returned timeout. After: 100.308 ms and |
| 173 | + 1100.151 ms, all finite/infinite/UINT32_MAX message wake checks passed. |
| 174 | +- C i386 glibc time64 independently failed before and passed after. i386 time32 |
| 175 | + and native x86_64 pass. Big-endian ARM musl time64 (nsec offset 12) also passes. |
| 176 | +- Rust ARM musl time32 and time64 public API waits and delayed-message checks pass; |
| 177 | + old wrapper with time64 returns in under 1 ms and fails the elapsed-time assertion. |
| 178 | +- C idle test records less than 1 ms process CPU per 100 ms wait under ARM QEMU; |
| 179 | + this is local blocking evidence, not a production performance estimate. |
| 180 | + |
| 181 | +Tests or equivalent validation: |
| 182 | + |
| 183 | +- Low-priority CMake configure/build and focused CTest: 7/7 passed (C SHM, |
| 184 | + timeout ABI, C service, Rust SHM, Go SHM, SHM interop, service SHM interop). |
| 185 | +- After Rust constructor updates: rebuilt and reran affected Rust SHM, C timeout |
| 186 | + and both interop suites: 4/4 passed. Interop covers all nine C/Rust/Go pairings. |
| 187 | +- Rust 1.91.0 and latest stable 1.98.1: 50 SHM unit tests and public-API |
| 188 | + integration fixture passed on each. Latest stable also passed both ARM musl |
| 189 | + time layouts and the Clippy correctness/suspicious gate. |
| 190 | +- C cross-ABI commands: run-shm-timeout-abi.sh with native cc, cc -m32, cc -m32 |
| 191 | + -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64, Zig ARM musl and Zig big-endian ARM musl. |
| 192 | +- run-rust-shm-timeout-abi.sh: both ARM libc crate time configurations pass. |
| 193 | +- Actionlint, ShellCheck, Rust format check, YAML parse and diff check pass. |
| 194 | +- Clippy correctness/suspicious gate passes; existing advisory warnings remain. |
| 195 | + Added a narrow documented allowance for field reassignment because the suggested |
| 196 | + struct literal fails to compile with libc's private time64 padding. |
| 197 | +- New GitHub job is configured but has not run remotely; local C glibc coverage |
| 198 | + uses i386 and local ARM C coverage uses musl. No CI result is claimed. |
| 199 | + |
| 200 | +Real-use evidence: |
| 201 | + |
| 202 | +- Public SHM server/client mappings with no traffic actually block; forked C |
| 203 | + peers and Rust thread peers wake finite/infinite/maximum-timeout receives. |
| 204 | +- Production hosts and downstream source were not modified or remeasured. |
| 205 | + |
| 206 | +Reviewer findings: |
| 207 | + |
| 208 | +- Assistant self-review checked timeout width bounds, null pointers, shared futex |
| 209 | + flags, x32 kernel word size, endian handling, Rust private padding, cleanup and |
| 210 | + CI prerequisites. Corrected missing explicit cross-libc headers in the CI install |
| 211 | + and ShellCheck's masked-command-status warning. No independent reviewer used. |
| 212 | + |
| 213 | +Same-failure scan: |
| 214 | + |
| 215 | +- Searched C/Rust/Go raw syscall and timespec use. Only C and Rust SHM wait wrappers |
| 216 | + passed libc timespec to raw timed syscalls; both repaired. Rust benchmark clock |
| 217 | + helper also needed Default construction for time64 compilation. Go uses kernel |
| 218 | + Timespec; fixed its architecture-dependent duration construction. Remaining |
| 219 | + independent 32-bit builds are represented by pending SOW-0037. |
| 220 | + |
| 221 | +Sensitive data gate: |
| 222 | + |
| 223 | +- Durable changes contain synthetic test paths and sanitized timing evidence, |
| 224 | + not host identities, private endpoints, raw investigation notes, personal data |
| 225 | + or credentials. Public musl source URL and source file paths are safe references. |
| 226 | + |
| 227 | +Artifact maintenance gate: |
| 228 | + |
| 229 | +- AGENTS.md: unchanged; responsibilities, protocol layers and low-priority workflow |
| 230 | + are preserved. No new project-wide guardrail is required. |
| 231 | +- Runtime project skills: unchanged; no downstream copy or preflight workflow changed. |
| 232 | +- Specs: updated docs/level1-posix-shm.md with local ABI invariant; no duplicate |
| 233 | + .agents/sow/specs document needed for an existing public contract. |
| 234 | +- End-user/operator docs: docs/level1-posix-shm.md includes reproducible C/Rust |
| 235 | + portability commands and explicitly limits emulator evidence. |
| 236 | +- End-user/operator skills: docs/netipc-integrator-skill.md adds target-libc timeout |
| 237 | + validation guidance; it does not claim complete 32-bit language support. |
| 238 | +- SOW lifecycle: SOW-0036 closes with the implementation in the same commit; |
| 239 | + SOW-0037 remains open/pending for independent platform build gaps. Existing |
| 240 | + current SOWs remain paused. No archived TODO history changed. |
| 241 | + |
| 242 | +Specs update: authoritative SHM spec updated as above; wire layout unchanged. |
| 243 | + |
| 244 | +Project skills update: runtime vendoring skill unchanged because no vendoring occurs. |
| 245 | + |
| 246 | +End-user/operator docs update: public ABI requirement and test commands updated. |
| 247 | + |
| 248 | +End-user/operator skills update: integrator guide points consumers to target testing. |
| 249 | + |
| 250 | +Lessons: |
| 251 | + |
| 252 | +- A timeout error alone cannot prove real waiting. Check elapsed time and wakeup. |
| 253 | +- libc time types are not raw syscall layouts; test both widths and endian variants. |
| 254 | + |
| 255 | +Follow-up mapping: |
| 256 | + |
| 257 | +- Timeout marshalling, tests, docs and CI: implemented in SOW-0036. |
| 258 | +- Go UDS 32-bit and Rust pthread_t unit-test build issues: tracked in SOW-0037. |
| 259 | +- Deployment, host configuration changes and CPU claims: outside the source-repair |
| 260 | + scope; no promised production outcome is marked complete here. |
| 261 | + |
| 262 | +## Outcome |
| 263 | + |
| 264 | +C and Rust marshal the selected futex timeout ABI explicitly. Go constructs its |
| 265 | +kernel Timespec portably. Subsecond/multisecond waiting and delayed-peer wakeup |
| 266 | +are covered by real cross-ABI execution, native tests and CI configuration. |
| 267 | +Public APIs, wire layout and service polling intervals are unchanged. Production |
| 268 | +CPU reduction remains unmeasured because deployment was outside this task. |
| 269 | + |
| 270 | +## Lessons Extracted |
| 271 | + |
| 272 | +Timeout error assertions alone cannot distinguish real waiting from immediate expiry. |
| 273 | + |
| 274 | +## Followup |
| 275 | + |
| 276 | +Independent Go 32-bit UDS and Rust pthread_t unit-test build failures are tracked |
| 277 | +in pending SOW-0037. No unrepresented deferred implementation remains. |
| 278 | + |
| 279 | +## Regression Log |
| 280 | + |
| 281 | +No prior completed time64 repair exists; this is a latent initial defect. |
0 commit comments