1 package main
2 3 import (
4 "os"
5 "runtime"
6 "unsafe"
7 )
8 9 // Milestone-2 verification for the spawn-inherited lockdown channel.
10 //
11 // Layout:
12 //
13 // 1. Parent creates a unidirectional pipe via runtime.MakePipe.
14 // 2. Parent forks. The child inherits the pipe fds (fork copies the fd table).
15 // 3. Child closes the read end, calls runtime.SetSecureLockdownFd(write),
16 // allocates a guarded arena, writes a recognizable secret pattern, then
17 // deliberately reads past the guard page to trip SIGSEGV. The signal
18 // handler wipes the arena and writes MOXIE_SECALLOC_LOCKDOWN to the
19 // write fd of the pipe — NOT to stderr.
20 // 4. Parent closes the write end, reads from the read end, waits for the
21 // child to die, and prints LOCKDOWN_RECEIVED if the marker arrived via
22 // the inherited pipe.
23 //
24 // The harness verifies:
25 // - exit status of the parent is 0
26 // - parent stdout contains LOCKDOWN_RECEIVED (so the byte traversed the pipe)
27 // - parent stdout contains CHILD_EXITED_SIGSEGV (child died on the guard page)
28 // - the raw secret pattern does NOT appear anywhere in parent stdout/stderr
29 //
30 // Why bare fork instead of spawn: spawn requires moxie.Codec types and a
31 // dispatch loop. M2.2 is about verifying the lockdown fd is routable to a
32 // real cross-process channel, not about exercising spawn. Once the routing
33 // is proven, integration with spawn is a separate, smaller step.
34 35 func main() {
36 read, write := runtime.MakePipe()
37 if read < 0 || write < 0 {
38 os.Stderr.Write([]byte("FAIL: MakePipe failed\n"))
39 os.Exit(1)
40 }
41 42 pid := runtime.Fork()
43 if pid < 0 {
44 os.Stderr.Write([]byte("FAIL: Fork failed\n"))
45 os.Exit(1)
46 }
47 48 if pid == 0 {
49 // Child path.
50 runtime.Close(read)
51 runtime.SetSecureLockdownFd(write)
52 53 secret := []byte{:32, secure}
54 pattern := []byte("MOXIE_SECRET_PAYLOAD_32_BYTES_AA")
55 copy(secret, pattern)
56 57 // Trip the trailing guard page exactly as the M1 test does.
58 ptr := (*byte)(unsafe.Add(unsafe.Pointer(&secret[0]), 4096))
59 sink := *ptr
60 os.Stdout.Write([]byte{sink})
61 os.Stdout.Write([]byte("UNREACHABLE\n"))
62 os.Exit(0)
63 }
64 65 // Parent path.
66 runtime.Close(write)
67 68 // Read up to 64 bytes from the lockdown pipe. The marker is
69 // "MOXIE_SECALLOC_LOCKDOWN\n" (24 bytes).
70 rf := os.NewFile(uintptr(read), "lockdown")
71 buf := make([]byte, 64)
72 n, _ := rf.Read(buf)
73 74 status := runtime.Waitpid(pid)
75 76 if n > 0 {
77 // Look for the marker substring in what we received.
78 marker := []byte("MOXIE_SECALLOC_LOCKDOWN")
79 got := buf[:n]
80 found := false
81 for i := 0; i+len(marker) <= len(got); i++ {
82 eq := true
83 for j := 0; j < len(marker); j++ {
84 if got[i+j] != marker[j] {
85 eq = false
86 break
87 }
88 }
89 if eq {
90 found = true
91 break
92 }
93 }
94 if found {
95 os.Stdout.Write([]byte("LOCKDOWN_RECEIVED\n"))
96 } else {
97 os.Stdout.Write([]byte("FAIL: pipe data did not contain marker\n"))
98 }
99 } else {
100 os.Stdout.Write([]byte("FAIL: pipe yielded no bytes\n"))
101 }
102 103 // status is the raw waitpid value: low 7 bits = signal if signalled,
104 // bit 7 = core dump flag, bits 8-15 = exit status if exited normally.
105 // SIGSEGV = 11. We just check the low 7 bits.
106 if status >= 0 && (status&0x7f) == 11 {
107 os.Stdout.Write([]byte("CHILD_EXITED_SIGSEGV\n"))
108 } else {
109 os.Stdout.Write([]byte("FAIL: child did not die on SIGSEGV (status="))
110 writeInt(status)
111 os.Stdout.Write([]byte(")\n"))
112 }
113 }
114 115 // writeInt writes a small int as decimal to stdout without using fmt
116 // (which we deliberately avoid here to keep the test minimal).
117 func writeInt(n int32) {
118 if n == 0 {
119 os.Stdout.Write([]byte("0"))
120 return
121 }
122 if n < 0 {
123 os.Stdout.Write([]byte("-"))
124 n = -n
125 }
126 var buf [12]byte
127 i := len(buf)
128 for n > 0 {
129 i--
130 buf[i] = byte('0' + n%10)
131 n /= 10
132 }
133 os.Stdout.Write(buf[i:])
134 }
135