Source file src/runtime/secret/testdata/crash.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"runtime"
    12  	"runtime/debug"
    13  	"runtime/secret"
    14  	"sync"
    15  	"syscall"
    16  	"time"
    17  	_ "unsafe"
    18  	"weak"
    19  )
    20  
    21  // Same secret as in ../../crash_test.go
    22  var secretStore = [8]byte{
    23  	0x00,
    24  	0x81,
    25  	0xa0,
    26  	0xc6,
    27  	0xb3,
    28  	0x01,
    29  	0x66,
    30  	0x53,
    31  }
    32  
    33  func main() {
    34  	enableCore()
    35  	useSecretProc()
    36  	// clear out secret. That way we don't have
    37  	// to figure out which secret is the allowed
    38  	// source
    39  	clear(secretStore[:])
    40  	panic("terminate")
    41  }
    42  
    43  // Copied from runtime/runtime-gdb_unix_test.go
    44  func enableCore() {
    45  	debug.SetTraceback("crash")
    46  
    47  	var lim syscall.Rlimit
    48  	err := syscall.Getrlimit(syscall.RLIMIT_CORE, &lim)
    49  	if err != nil {
    50  		panic(fmt.Sprintf("error getting rlimit: %v", err))
    51  	}
    52  	lim.Cur = lim.Max
    53  	fmt.Fprintf(os.Stderr, "Setting RLIMIT_CORE = %+#v\n", lim)
    54  	err = syscall.Setrlimit(syscall.RLIMIT_CORE, &lim)
    55  	if err != nil {
    56  		panic(fmt.Sprintf("error setting rlimit: %v", err))
    57  	}
    58  }
    59  
    60  // useSecretProc does 5 seconds of work, using the secret value
    61  // inside secret.Do in a bunch of ways.
    62  func useSecretProc() {
    63  	stop := make(chan bool)
    64  	var wg sync.WaitGroup
    65  
    66  	for i := 0; i < 4; i++ {
    67  		wg.Add(1)
    68  		go func() {
    69  			time.Sleep(1 * time.Second)
    70  			for {
    71  				select {
    72  				case <-stop:
    73  					wg.Done()
    74  					return
    75  				default:
    76  					secret.Do(func() {
    77  						// Copy key into a variable-sized heap allocation.
    78  						// This both puts secrets in heap objects,
    79  						// and more generally just causes allocation,
    80  						// which forces garbage collection, which
    81  						// requires interrupts and the like.
    82  						s := bytes.Repeat(secretStore[:], 1+i*2)
    83  						// Also spam the secret across all registers.
    84  						useSecret(s)
    85  					})
    86  				}
    87  			}
    88  		}()
    89  	}
    90  
    91  	// Send some allocations over a channel. This does 2 things:
    92  	// 1) forces some GCs to happen
    93  	// 2) causes more scheduling noise (Gs moving between Ms, etc.)
    94  	c := make(chan []byte)
    95  	wg.Add(2)
    96  	go func() {
    97  		for {
    98  			select {
    99  			case <-stop:
   100  				wg.Done()
   101  				return
   102  			case c <- make([]byte, 256):
   103  			}
   104  		}
   105  	}()
   106  	go func() {
   107  		for {
   108  			select {
   109  			case <-stop:
   110  				wg.Done()
   111  				return
   112  			case <-c:
   113  			}
   114  		}
   115  	}()
   116  
   117  	time.Sleep(5 * time.Second)
   118  	close(stop)
   119  	wg.Wait()
   120  	// use a weak reference for ensuring that the GC has cleared everything
   121  	// Use a large value to avoid the tiny allocator.
   122  	w := weak.Make(new([2048]byte))
   123  	// 20 seems like a decent amount?
   124  	for i := 0; i < 20; i++ {
   125  		runtime.GC() // GC should clear any secret heap objects and clear out scheduling buffers.
   126  		if w.Value() == nil {
   127  			fmt.Fprintf(os.Stderr, "number of GCs %v\n", i+1)
   128  			return
   129  		}
   130  	}
   131  	fmt.Fprintf(os.Stderr, "GC didn't clear out in time\n")
   132  	// This will cause the core dump to happen with the sentinel value still in memory
   133  	// so we will detect the fault.
   134  	panic("fault")
   135  }
   136  

View as plain text