1
2
3
4
5
6
7
8
9
10
11
12
13
14 package secret
15
16 import (
17 "runtime"
18 "strings"
19 "testing"
20 "time"
21 "unsafe"
22 "weak"
23 )
24
25 type secretType int64
26
27 const secretValue = 0x53c237_53c237
28
29
30 type S [100]secretType
31
32
33
34
35 func makeS() S {
36
37
38 var s S
39 for i := range s {
40 s[i] = secretValue
41 }
42 return s
43 }
44
45
46
47
48 func heapS() *S {
49
50 s := makeS()
51 return &s
52 }
53
54
55
56
57 func heapSTiny() *secretType {
58 s := new(secretType(secretValue))
59 return s
60 }
61
62
63
64
65
66 func TestHeap(t *testing.T) {
67 var addr uintptr
68 var p weak.Pointer[S]
69 Do(func() {
70 sp := heapS()
71 addr = uintptr(unsafe.Pointer(sp))
72 p = weak.Make(sp)
73 })
74 waitCollected(t, p)
75
76
77 checkRangeForSecret(t, addr, addr+unsafe.Sizeof(S{}))
78
79 checkStackForSecret(t)
80 }
81
82 func TestHeapTiny(t *testing.T) {
83 var addr uintptr
84 var p weak.Pointer[secretType]
85 Do(func() {
86 sp := heapSTiny()
87 addr = uintptr(unsafe.Pointer(sp))
88 p = weak.Make(sp)
89 })
90 waitCollected(t, p)
91
92
93 checkRangeForSecret(t, addr, addr+unsafe.Sizeof(secretType(0)))
94
95 checkStackForSecret(t)
96 }
97
98
99
100
101 func TestStack(t *testing.T) {
102 checkStackForSecret(t)
103
104 Do(func() {
105 s := makeS()
106 use(&s)
107 })
108
109 checkStackForSecret(t)
110 }
111
112
113 func use(s *S) {
114
115 }
116
117
118
119 func TestStackCopy(t *testing.T) {
120 checkStackForSecret(t)
121
122 var lo, hi uintptr
123 Do(func() {
124
125 s := makeS()
126 use(&s)
127
128 lo, hi = getStack()
129
130 growStack()
131 })
132 checkRangeForSecret(t, lo, hi)
133 checkStackForSecret(t)
134 }
135
136 func growStack() {
137 growStack1(1000)
138 }
139 func growStack1(n int) {
140 if n == 0 {
141 return
142 }
143 growStack1(n - 1)
144 }
145
146 func TestPanic(t *testing.T) {
147 checkStackForSecret(t)
148
149 defer func() {
150 checkStackForSecret(t)
151
152 p := recover()
153 if p == nil {
154 t.Errorf("panic squashed")
155 return
156 }
157 var e error
158 var ok bool
159 if e, ok = p.(error); !ok {
160 t.Errorf("panic not an error")
161 }
162 if !strings.Contains(e.Error(), "divide by zero") {
163 t.Errorf("panic not a divide by zero error: %s", e.Error())
164 }
165 var pcs [10]uintptr
166 n := runtime.Callers(0, pcs[:])
167 frames := runtime.CallersFrames(pcs[:n])
168 for {
169 frame, more := frames.Next()
170 if strings.Contains(frame.Function, "dividePanic") {
171 t.Errorf("secret function in traceback")
172 }
173 if !more {
174 break
175 }
176 }
177 }()
178 Do(dividePanic)
179 }
180
181 func dividePanic() {
182 s := makeS()
183 use(&s)
184 _ = 8 / zero
185 }
186
187 var zero int
188
189 func TestGoExit(t *testing.T) {
190 checkStackForSecret(t)
191
192 c := make(chan uintptr, 2)
193
194 go func() {
195
196 defer func() {
197
198
199 lo, hi := getStack()
200 c <- lo
201 c <- hi
202 }()
203 Do(func() {
204 s := makeS()
205 use(&s)
206
207
208
209 loadRegisters(unsafe.Pointer(&s))
210 runtime.Goexit()
211 })
212 t.Errorf("goexit didn't happen")
213 }()
214 lo := <-c
215 hi := <-c
216
217
218 time.Sleep(1 * time.Millisecond)
219
220 checkRangeForSecret(t, lo, hi)
221
222 var spillArea [64]secretType
223 n := spillRegisters(unsafe.Pointer(&spillArea))
224 if n > unsafe.Sizeof(spillArea) {
225 t.Fatalf("spill area overrun %d\n", n)
226 }
227 for i, v := range spillArea {
228 if v == secretValue {
229 t.Errorf("secret found in spill slot %d", i)
230 }
231 }
232 }
233
234 func checkStackForSecret(t *testing.T) {
235 t.Helper()
236 lo, hi := getStack()
237 checkRangeForSecret(t, lo, hi)
238 }
239 func checkRangeForSecret(t *testing.T, lo, hi uintptr) {
240 t.Helper()
241 for p := lo; p < hi; p += unsafe.Sizeof(secretType(0)) {
242 v := *(*secretType)(unsafe.Pointer(p))
243 if v == secretValue {
244 t.Errorf("secret found in [%x,%x] at %x", lo, hi, p)
245 }
246 }
247 }
248
249 func waitCollected[P any](t *testing.T, ptr weak.Pointer[P]) {
250 t.Helper()
251 i := 0
252 for ptr.Value() != nil {
253 runtime.GC()
254 i++
255
256 if i > 20 {
257 t.Errorf("value was never collected")
258 }
259 }
260 t.Logf("number of cycles until collection: %d", i)
261 }
262
263 func TestRegisters(t *testing.T) {
264 Do(func() {
265 s := makeS()
266 loadRegisters(unsafe.Pointer(&s))
267 })
268 var spillArea [64]secretType
269 n := spillRegisters(unsafe.Pointer(&spillArea))
270 if n > unsafe.Sizeof(spillArea) {
271 t.Fatalf("spill area overrun %d\n", n)
272 }
273 for i, v := range spillArea {
274 if v == secretValue {
275 t.Errorf("secret found in spill slot %d", i)
276 }
277 }
278 }
279
280 func TestSignalStacks(t *testing.T) {
281 Do(func() {
282 s := makeS()
283 loadRegisters(unsafe.Pointer(&s))
284
285
286 func() {
287 defer func() {
288 x := recover()
289 if x == nil {
290 panic("did not get panic")
291 }
292 }()
293 var p *int
294 *p = 20
295 }()
296 })
297
298
299 runtime.GC()
300 stk := make([]stack, 0, 100)
301 stk = appendSignalStacks(stk)
302 for _, s := range stk {
303 checkRangeForSecret(t, s.lo, s.hi)
304 }
305 }
306
307
308 func getStack() (uintptr, uintptr)
309
310
311
312 type stack struct {
313 lo uintptr
314 hi uintptr
315 }
316
317 func appendSignalStacks([]stack) []stack
318
View as plain text