feat(verify): ABI checks on arm64, riscv64 and loong64
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -5,17 +5,6 @@
|
||||
|
||||
package verify
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// abiResult records register-clobber violations detected by the ABI-checking
|
||||
// trampoline. Bit 0: BP clobbered. Bit 1: R14 clobbered.
|
||||
var abiResult uint64
|
||||
|
||||
// savedBP holds the caller's frame pointer across the ABI-checked JIT call.
|
||||
// Written and read by enterJITChecked/leaveJITChecked (abi_amd64.s); no Go
|
||||
// code references it, which GoLand cannot see inside assembly.
|
||||
@@ -50,93 +39,3 @@ func enterJITChecked(fn uintptr, stack uintptr)
|
||||
//lint:ignore U1000 the assembly obtains this address through leaveCheckedPtr
|
||||
//go:nosplit
|
||||
func leaveJITCheckedRaw()
|
||||
|
||||
// ABIReport describes the result of an ABI-checking call.
|
||||
type ABIReport struct {
|
||||
BPClobbered bool // BP was modified by the function
|
||||
R14Clobbered bool // R14 (goroutine pointer) was modified
|
||||
RedZoneHit bool // the 128-byte red zone below SP was written
|
||||
}
|
||||
|
||||
// OK returns true when no violations were detected.
|
||||
func (r ABIReport) OK() bool {
|
||||
return !r.BPClobbered && !r.R14Clobbered && !r.RedZoneHit
|
||||
}
|
||||
|
||||
// String returns a human-readable summary.
|
||||
func (r ABIReport) String() string {
|
||||
if r.OK() {
|
||||
return "ABI clean"
|
||||
}
|
||||
s := "ABI violation:"
|
||||
if r.BPClobbered {
|
||||
s += " BP clobbered"
|
||||
}
|
||||
if r.R14Clobbered {
|
||||
s += " R14 clobbered"
|
||||
}
|
||||
if r.RedZoneHit {
|
||||
s += " red-zone written"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// redZoneSize is the System V AMD64 red zone: 128 bytes below SP that a
|
||||
// leaf function may use without adjusting SP. Go does not use the red zone,
|
||||
// so any write there is a bug.
|
||||
const redZoneSize = 128
|
||||
|
||||
// redZoneFill is the byte pattern used to detect red-zone writes.
|
||||
const redZoneFill = 0xA5
|
||||
|
||||
// CallChecked invokes the function at fnAddr with ABI sentinels and a
|
||||
// red-zone canary, returning both the argument block (with results) and an
|
||||
// ABIReport.
|
||||
func CallChecked(fnAddr uintptr, args []byte) ([]byte, ABIReport, error) {
|
||||
report := ABIReport{}
|
||||
|
||||
// Reset the global result.
|
||||
abiResult = 0
|
||||
|
||||
// Prepare the stack: [red-zone canary][padding][leaveJITCheckedRaw][args...]
|
||||
// The red zone sits below the initial SP, so the function would have to
|
||||
// write below SP to corrupt it.
|
||||
totalSize := redZoneSize + stackPad + 8 + len(args) + 64
|
||||
stackMem, err := syscall.Mmap(-1, 0, totalSize,
|
||||
syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_PRIVATE|syscall.MAP_ANON)
|
||||
if err != nil {
|
||||
return nil, report, fmt.Errorf("verify: stack mmap: %w", err)
|
||||
}
|
||||
defer func() { _ = syscall.Munmap(stackMem) }()
|
||||
|
||||
// Fill the red zone with the canary pattern.
|
||||
for i := range redZoneSize {
|
||||
stackMem[i] = redZoneFill
|
||||
}
|
||||
|
||||
// Return address and args after the red zone and padding.
|
||||
retOff := redZoneSize + stackPad
|
||||
binary.LittleEndian.PutUint64(stackMem[retOff:retOff+8], uint64(leaveCheckedPtr))
|
||||
copy(stackMem[retOff+8:], args)
|
||||
|
||||
stackBase := uintptr(unsafe.Pointer(&stackMem[retOff]))
|
||||
enterJITChecked(fnAddr, stackBase)
|
||||
|
||||
// Read the register-clobber result.
|
||||
res := abiResult
|
||||
report.BPClobbered = res&1 != 0
|
||||
report.R14Clobbered = res&2 != 0
|
||||
|
||||
// Check the red zone.
|
||||
for i := range redZoneSize {
|
||||
if stackMem[i] != redZoneFill {
|
||||
report.RedZoneHit = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Copy out the argument area.
|
||||
out := make([]byte, len(args))
|
||||
copy(out, stackMem[retOff+8:retOff+8+len(args)])
|
||||
return out, report, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user