feat(verify): combine ABI checks with fuzzing for deep-path testing
This commit is contained in:
@@ -5,6 +5,7 @@ package verify
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-devkit/asm"
|
||||
@@ -114,6 +115,66 @@ func (k *Kernel) CallFuncChecked(name string, args []byte) ([]byte, ABIReport, e
|
||||
return CallChecked(fnAddr, args)
|
||||
}
|
||||
|
||||
// FuzzFuncCheckedByName is like FuzzFuncChecked but extracts the signature
|
||||
// from the source code internally.
|
||||
func (k *Kernel) FuzzFuncCheckedByName(name, src string, iterations int, seed int64) FuzzResult {
|
||||
result := FuzzResult{Func: name, Iterations: iterations}
|
||||
sig, ok := ExtractSignatures(src)[name]
|
||||
if !ok {
|
||||
result.Mismatches = iterations
|
||||
result.FirstFail = "no // func signature found"
|
||||
return result
|
||||
}
|
||||
return k.FuzzFuncChecked(name, sig, iterations, seed)
|
||||
}
|
||||
|
||||
// FuzzFuncChecked combines fuzzing with ABI checks: it generates varied
|
||||
// inputs and verifies that callee-saved registers and the red zone are
|
||||
// preserved even on deep execution paths (not just early exits).
|
||||
func (k *Kernel) FuzzFuncChecked(name string, sig funcSig, iterations int, seed int64) FuzzResult {
|
||||
result := FuzzResult{Func: name, Iterations: iterations}
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
|
||||
fl, err := k.Func(name)
|
||||
if err != nil {
|
||||
result.Mismatches = iterations
|
||||
result.FirstFail = err.Error()
|
||||
return result
|
||||
}
|
||||
|
||||
violations := 0
|
||||
for i := 0; i < iterations; i++ {
|
||||
gasmArgs, _, bufs := genDualArgs(rng, sig, fl.Args)
|
||||
result.CrashInput = gasmArgs
|
||||
|
||||
_, report, err := k.CallFuncChecked(name, gasmArgs)
|
||||
if err != nil {
|
||||
result.Mismatches++
|
||||
if result.FirstFail == "" {
|
||||
result.FirstFail = fmt.Sprintf("iter %d: call: %v", i, err)
|
||||
}
|
||||
releaseBufs(bufs)
|
||||
continue
|
||||
}
|
||||
|
||||
if !report.OK() {
|
||||
violations++
|
||||
result.Mismatches++
|
||||
if result.FirstFail == "" {
|
||||
result.FirstFail = fmt.Sprintf("iter %d: %s", i, report.String())
|
||||
}
|
||||
} else {
|
||||
result.Matches++
|
||||
}
|
||||
releaseBufs(bufs)
|
||||
}
|
||||
|
||||
if violations > 0 && result.FirstFail == "" {
|
||||
result.FirstFail = fmt.Sprintf("%d ABI violations across %d iterations", violations, iterations)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Close releases the executable mapping.
|
||||
func (k *Kernel) Close() {
|
||||
if k.exec != nil {
|
||||
|
||||
Reference in New Issue
Block a user