···11+# Io.Evented + ReleaseSafe: GPF in fiber.zig contextSwitch on x86_64-linux
22+33+`Io.Evented` crashes with a general protection fault under ReleaseSafe on x86_64-linux. Debug, ReleaseFast, and ReleaseSmall all pass. Tested on unpatched zig — no modifications to the stdlib.
44+55+## Reproduction
66+77+```zig
88+const std = @import("std");
99+const Io = std.Io;
1010+1111+var evented: Io.Evented = undefined;
1212+1313+var debug_threaded_io: Io.Threaded = undefined;
1414+pub const std_options_debug_threaded_io: ?*Io.Threaded = &debug_threaded_io;
1515+1616+fn fiberReturn(_: Io) void {}
1717+1818+pub fn main() !void {
1919+ const allocator = std.heap.c_allocator;
2020+ debug_threaded_io = Io.Threaded.init(allocator, .{});
2121+ try Io.Evented.init(&evented, allocator, .{});
2222+ const io = evented.io();
2323+2424+ var f = try io.concurrent(fiberReturn, .{io});
2525+ io.sleep(Io.Duration.fromMilliseconds(10), .awake) catch {};
2626+ f.cancel(io);
2727+}
2828+```
2929+3030+```
3131+$ zig build-exe -OReleaseSafe repro.zig -lc && ./repro
3232+General protection exception (no address available)
3333+lib/std/Io/fiber.zig:30:20: 0x1079589 in contextSwitch
3434+lib/std/Io/Uring.zig:1142:12: 0x109cc86 in mainIdle
3535+```
3636+3737+| Mode | Result |
3838+|------|--------|
3939+| Debug | pass |
4040+| ReleaseFast | pass |
4141+| ReleaseSmall | pass |
4242+| ReleaseSafe | GPF |
4343+4444+## Environment
4545+4646+- zig 0.16.0-dev.3059+42e33db9d, unmodified
4747+- x86_64-linux, kernel 6.8.0-101-generic (Debian bookworm, glibc)
4848+4949+## What we've observed
5050+5151+The crash is at the inline asm in `fiber.zig` contextSwitch (x86_64 path, line 244). This gets inlined into `Uring.idle`.
5252+5353+Comparing the disassembly of `Uring.idle` between ReleaseFast and ReleaseSafe, the difference we see is in how `%rsi` is set up before the inline asm block. The asm uses `"{rsi}" (s)` as an input constraint:
5454+5555+**ReleaseFast** — there's a `lea -0x80(%rbp),%rsi` that loads the SwitchMessage address into `%rsi` before the asm block.
5656+5757+**ReleaseSafe** — the corresponding `lea` appears to be absent. `%rsi` seems to hold a stale value from a prior function call (`Thread.current`, which is caller-saved). The ReleaseSafe prologue includes `__zig_probe_stack` and a stack canary load (`fs:0x28`) that aren't present in ReleaseFast.
5858+5959+We're not certain this is the full picture — we may be misreading the disassembly or missing something about how the register constraint interacts with the surrounding code. But the crash is consistent and the repro is deterministic.
6060+6161+## Context
6262+6363+We're building an AT Protocol relay ([zlay](https://tangled.sh/zzstoatzz.io/zlay)) that runs ~2,800 concurrent fibers on `Io.Evented`. We've been working around this by building with ReleaseFast, but recently a separate bug (a websocket off-by-one) manifested as a silent SIGSEGV under ReleaseFast for weeks. ReleaseSafe would have caught it immediately as a bounds-check panic with a stack trace.
6464+6565+We understand Evented is experimental. Happy to provide more information or test patches.