feat(verify): add runtime ABI checks with sentinel registers and red-zone canary
Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package verify
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func loadABIKernel(t *testing.T) *Kernel {
|
||||
t.Helper()
|
||||
k, err := Load("../testdata/verify/abi_amd64.s")
|
||||
if err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
t.Cleanup(k.Close)
|
||||
return k
|
||||
}
|
||||
|
||||
func TestABIClean(t *testing.T) {
|
||||
k := loadABIKernel(t)
|
||||
|
||||
args := make([]byte, 24)
|
||||
PutUint64(args, 0, 3)
|
||||
PutUint64(args, 8, 4)
|
||||
|
||||
out, report, err := k.CallFuncChecked("cleanAdd", args)
|
||||
if err != nil {
|
||||
t.Fatalf("CallFuncChecked: %v", err)
|
||||
}
|
||||
if got := int64(GetUint64(out, 16)); got != 7 {
|
||||
t.Errorf("cleanAdd(3, 4) = %d, want 7", got)
|
||||
}
|
||||
if !report.OK() {
|
||||
t.Errorf("cleanAdd: %s", report)
|
||||
}
|
||||
}
|
||||
|
||||
func TestABIBPClobbered(t *testing.T) {
|
||||
k := loadABIKernel(t)
|
||||
|
||||
args := make([]byte, 16)
|
||||
PutUint64(args, 0, 42)
|
||||
|
||||
out, report, err := k.CallFuncChecked("dirtyBP", args)
|
||||
if err != nil {
|
||||
t.Fatalf("CallFuncChecked: %v", err)
|
||||
}
|
||||
if got := int64(GetUint64(out, 8)); got != 42 {
|
||||
t.Errorf("dirtyBP(42) = %d, want 42", got)
|
||||
}
|
||||
if !report.BPClobbered {
|
||||
t.Error("dirtyBP: expected BP clobbered, but report says clean")
|
||||
}
|
||||
if report.R14Clobbered {
|
||||
t.Error("dirtyBP: R14 should not be clobbered")
|
||||
}
|
||||
}
|
||||
|
||||
func TestABIR14Clobbered(t *testing.T) {
|
||||
k := loadABIKernel(t)
|
||||
|
||||
args := make([]byte, 16)
|
||||
PutUint64(args, 0, 99)
|
||||
|
||||
out, report, err := k.CallFuncChecked("dirtyR14", args)
|
||||
if err != nil {
|
||||
t.Fatalf("CallFuncChecked: %v", err)
|
||||
}
|
||||
if got := int64(GetUint64(out, 8)); got != 99 {
|
||||
t.Errorf("dirtyR14(99) = %d, want 99", got)
|
||||
}
|
||||
if !report.R14Clobbered {
|
||||
t.Error("dirtyR14: expected R14 clobbered, but report says clean")
|
||||
}
|
||||
if report.BPClobbered {
|
||||
t.Error("dirtyR14: BP should not be clobbered")
|
||||
}
|
||||
}
|
||||
|
||||
// TestABILZ4Kernels verifies that the production go-lz4 kernels are ABI-clean:
|
||||
// they preserve BP and R14 and do not write into the red zone.
|
||||
func TestABILZ4Kernels(t *testing.T) {
|
||||
k := loadLZ4Kernel(t)
|
||||
|
||||
// wideCopyAVX2 with a real copy.
|
||||
src := make([]byte, 128)
|
||||
for i := range src {
|
||||
src[i] = byte(i)
|
||||
}
|
||||
dst := make([]byte, 128)
|
||||
|
||||
args := make([]byte, 48)
|
||||
PutPtr(args, 0, unsafe.Pointer(&dst[0]))
|
||||
PutUint64(args, 8, 128)
|
||||
PutUint64(args, 16, 128)
|
||||
PutPtr(args, 24, unsafe.Pointer(&src[0]))
|
||||
PutUint64(args, 32, 128)
|
||||
PutUint64(args, 40, 128)
|
||||
|
||||
_, report, err := k.CallFuncChecked("wideCopyAVX2", args)
|
||||
if err != nil {
|
||||
t.Fatalf("CallFuncChecked(wideCopyAVX2): %v", err)
|
||||
}
|
||||
if !report.OK() {
|
||||
t.Errorf("wideCopyAVX2: %s", report)
|
||||
}
|
||||
|
||||
// decodeBlockAVX2 with a simple block.
|
||||
decSrc := []byte{0x50, 'H', 'e', 'l', 'l', 'o'}
|
||||
decDst := make([]byte, 64)
|
||||
|
||||
decArgs := make([]byte, 64)
|
||||
PutPtr(decArgs, 0, unsafe.Pointer(&decSrc[0]))
|
||||
PutUint64(decArgs, 8, uint64(len(decSrc)))
|
||||
PutUint64(decArgs, 16, uint64(cap(decSrc)))
|
||||
PutPtr(decArgs, 24, unsafe.Pointer(&decDst[0]))
|
||||
PutUint64(decArgs, 32, uint64(len(decDst)))
|
||||
PutUint64(decArgs, 40, uint64(cap(decDst)))
|
||||
|
||||
_, report, err = k.CallFuncChecked("decodeBlockAVX2", decArgs)
|
||||
if err != nil {
|
||||
t.Fatalf("CallFuncChecked(decodeBlockAVX2): %v", err)
|
||||
}
|
||||
if !report.OK() {
|
||||
t.Errorf("decodeBlockAVX2: %s", report)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallFuncCheckedErrors(t *testing.T) {
|
||||
k := loadABIKernel(t)
|
||||
|
||||
// Nonexistent function.
|
||||
_, _, err := k.CallFuncChecked("nope", make([]byte, 8))
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nonexistent function")
|
||||
}
|
||||
|
||||
// Arg block too small.
|
||||
_, _, err = k.CallFuncChecked("cleanAdd", make([]byte, 8))
|
||||
if err == nil {
|
||||
t.Fatal("expected error for too-small arg block")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user