Files
gasm-sdk/verify/abi.go
T

72 lines
2.2 KiB
Go
Raw Normal View History

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
//go:build amd64 || arm64 || riscv64 || loong64
package verify
import (
"encoding/binary"
"fmt"
"syscall"
"unsafe"
)
// CallChecked invokes the function at fnAddr with ABI sentinels and a
// canary below SP, returning both the argument block (with results) and an
// ABIReport.
//
// The architecture-specific parts live in abi_<arch>.s: enterJITChecked
// plants sentinels in the registers the Go ABI fixes across calls (the
// frame pointer and the goroutine pointer) before switching to the
// prepared stack, and the raw return trampoline leaveJITCheckedRaw
// compares them and records violations in abiResult.
func CallChecked(fnAddr uintptr, args []byte) ([]byte, ABIReport, error) {
report := ABIReport{}
// Reset the global result.
abiResult = 0
// Prepare the stack: [canary][padding][leaveJITCheckedRaw][args...]
// The canary 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 canary window with the detection pattern.
for i := range redZoneSize {
stackMem[i] = redZoneFill
}
// Return address and args after the canary 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.FPClobbered = res&1 != 0
report.GClobbered = res&2 != 0
// Check the canary window.
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
}