github.com/lzhfromustc/gofuzz@v0.0.0-20211116160056-151b3108bbd1/runtime/export_debug_test.go (about)

     1  // Copyright 2018 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  // +build amd64
     6  // +build linux
     7  
     8  package runtime
     9  
    10  import (
    11  	"runtime/internal/sys"
    12  	"unsafe"
    13  )
    14  
    15  // InjectDebugCall injects a debugger call to fn into g. args must be
    16  // a pointer to a valid call frame (including arguments and return
    17  // space) for fn, or nil. tkill must be a function that will send
    18  // SIGTRAP to thread ID tid. gp must be locked to its OS thread and
    19  // running.
    20  //
    21  // On success, InjectDebugCall returns the panic value of fn or nil.
    22  // If fn did not panic, its results will be available in args.
    23  func InjectDebugCall(gp *g, fn, args interface{}, tkill func(tid int) error, returnOnUnsafePoint bool) (interface{}, error) {
    24  	if gp.lockedm == 0 {
    25  		return nil, plainError("goroutine not locked to thread")
    26  	}
    27  
    28  	tid := int(gp.lockedm.ptr().procid)
    29  	if tid == 0 {
    30  		return nil, plainError("missing tid")
    31  	}
    32  
    33  	f := efaceOf(&fn)
    34  	if f._type == nil || f._type.kind&kindMask != kindFunc {
    35  		return nil, plainError("fn must be a function")
    36  	}
    37  	fv := (*funcval)(f.data)
    38  
    39  	a := efaceOf(&args)
    40  	if a._type != nil && a._type.kind&kindMask != kindPtr {
    41  		return nil, plainError("args must be a pointer or nil")
    42  	}
    43  	argp := a.data
    44  	var argSize uintptr
    45  	if argp != nil {
    46  		argSize = (*ptrtype)(unsafe.Pointer(a._type)).elem.size
    47  	}
    48  
    49  	h := new(debugCallHandler)
    50  	h.gp = gp
    51  	// gp may not be running right now, but we can still get the M
    52  	// it will run on since it's locked.
    53  	h.mp = gp.lockedm.ptr()
    54  	h.fv, h.argp, h.argSize = fv, argp, argSize
    55  	h.handleF = h.handle // Avoid allocating closure during signal
    56  
    57  	defer func() { testSigtrap = nil }()
    58  	for i := 0; ; i++ {
    59  		testSigtrap = h.inject
    60  		noteclear(&h.done)
    61  		h.err = ""
    62  
    63  		if err := tkill(tid); err != nil {
    64  			return nil, err
    65  		}
    66  		// Wait for completion.
    67  		notetsleepg(&h.done, -1)
    68  		if h.err != "" {
    69  			switch h.err {
    70  			case "call not at safe point":
    71  				if returnOnUnsafePoint {
    72  					// This is for TestDebugCallUnsafePoint.
    73  					return nil, h.err
    74  				}
    75  				fallthrough
    76  			case "retry _Grunnable", "executing on Go runtime stack", "call from within the Go runtime":
    77  				// These are transient states. Try to get out of them.
    78  				if i < 100 {
    79  					usleep(100)
    80  					Gosched()
    81  					continue
    82  				}
    83  			}
    84  			return nil, h.err
    85  		}
    86  		return h.panic, nil
    87  	}
    88  }
    89  
    90  type debugCallHandler struct {
    91  	gp      *g
    92  	mp      *m
    93  	fv      *funcval
    94  	argp    unsafe.Pointer
    95  	argSize uintptr
    96  	panic   interface{}
    97  
    98  	handleF func(info *siginfo, ctxt *sigctxt, gp2 *g) bool
    99  
   100  	err       plainError
   101  	done      note
   102  	savedRegs sigcontext
   103  	savedFP   fpstate1
   104  }
   105  
   106  func (h *debugCallHandler) inject(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
   107  	switch h.gp.atomicstatus {
   108  	case _Grunning:
   109  		if getg().m != h.mp {
   110  			println("trap on wrong M", getg().m, h.mp)
   111  			return false
   112  		}
   113  		// Push current PC on the stack.
   114  		rsp := ctxt.rsp() - sys.PtrSize
   115  		*(*uint64)(unsafe.Pointer(uintptr(rsp))) = ctxt.rip()
   116  		ctxt.set_rsp(rsp)
   117  		// Write the argument frame size.
   118  		*(*uintptr)(unsafe.Pointer(uintptr(rsp - 16))) = h.argSize
   119  		// Save current registers.
   120  		h.savedRegs = *ctxt.regs()
   121  		h.savedFP = *h.savedRegs.fpstate
   122  		h.savedRegs.fpstate = nil
   123  		// Set PC to debugCallV1.
   124  		ctxt.set_rip(uint64(funcPC(debugCallV1)))
   125  		// Call injected. Switch to the debugCall protocol.
   126  		testSigtrap = h.handleF
   127  	case _Grunnable:
   128  		// Ask InjectDebugCall to pause for a bit and then try
   129  		// again to interrupt this goroutine.
   130  		h.err = plainError("retry _Grunnable")
   131  		notewakeup(&h.done)
   132  	default:
   133  		h.err = plainError("goroutine in unexpected state at call inject")
   134  		notewakeup(&h.done)
   135  	}
   136  	// Resume execution.
   137  	return true
   138  }
   139  
   140  func (h *debugCallHandler) handle(info *siginfo, ctxt *sigctxt, gp2 *g) bool {
   141  	// Sanity check.
   142  	if getg().m != h.mp {
   143  		println("trap on wrong M", getg().m, h.mp)
   144  		return false
   145  	}
   146  	f := findfunc(uintptr(ctxt.rip()))
   147  	if !(hasPrefix(funcname(f), "runtime.debugCall") || hasPrefix(funcname(f), "debugCall")) {
   148  		println("trap in unknown function", funcname(f))
   149  		return false
   150  	}
   151  	if *(*byte)(unsafe.Pointer(uintptr(ctxt.rip() - 1))) != 0xcc {
   152  		println("trap at non-INT3 instruction pc =", hex(ctxt.rip()))
   153  		return false
   154  	}
   155  
   156  	switch status := ctxt.rax(); status {
   157  	case 0:
   158  		// Frame is ready. Copy the arguments to the frame.
   159  		sp := ctxt.rsp()
   160  		memmove(unsafe.Pointer(uintptr(sp)), h.argp, h.argSize)
   161  		// Push return PC.
   162  		sp -= sys.PtrSize
   163  		ctxt.set_rsp(sp)
   164  		*(*uint64)(unsafe.Pointer(uintptr(sp))) = ctxt.rip()
   165  		// Set PC to call and context register.
   166  		ctxt.set_rip(uint64(h.fv.fn))
   167  		ctxt.regs().rcx = uint64(uintptr(unsafe.Pointer(h.fv)))
   168  	case 1:
   169  		// Function returned. Copy frame back out.
   170  		sp := ctxt.rsp()
   171  		memmove(h.argp, unsafe.Pointer(uintptr(sp)), h.argSize)
   172  	case 2:
   173  		// Function panicked. Copy panic out.
   174  		sp := ctxt.rsp()
   175  		memmove(unsafe.Pointer(&h.panic), unsafe.Pointer(uintptr(sp)), 2*sys.PtrSize)
   176  	case 8:
   177  		// Call isn't safe. Get the reason.
   178  		sp := ctxt.rsp()
   179  		reason := *(*string)(unsafe.Pointer(uintptr(sp)))
   180  		h.err = plainError(reason)
   181  		// Don't wake h.done. We need to transition to status 16 first.
   182  	case 16:
   183  		// Restore all registers except RIP and RSP.
   184  		rip, rsp := ctxt.rip(), ctxt.rsp()
   185  		fp := ctxt.regs().fpstate
   186  		*ctxt.regs() = h.savedRegs
   187  		ctxt.regs().fpstate = fp
   188  		*fp = h.savedFP
   189  		ctxt.set_rip(rip)
   190  		ctxt.set_rsp(rsp)
   191  		// Done
   192  		notewakeup(&h.done)
   193  	default:
   194  		h.err = plainError("unexpected debugCallV1 status")
   195  		notewakeup(&h.done)
   196  	}
   197  	// Resume execution.
   198  	return true
   199  }