// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause package verify import ( "os" "strings" "testing" ) func TestExtractSignatures(t *testing.T) { src := `// func add(a int64, b int64) int64 TEXT ·add(SB), NOSPLIT, $0-24 RET // func wideCopy(dst []byte, src []byte) TEXT ·wideCopy(SB), NOSPLIT, $0-48 RET ` sigs := ExtractSignatures(src) if len(sigs) != 2 { t.Fatalf("expected 2 signatures, got %d: %v", len(sigs), sigs) } add, ok := sigs["add"] if !ok { t.Fatal("add not found") } if len(add.params) != 2 { t.Errorf("add params: got %d, want 2", len(add.params)) } wc, ok := sigs["wideCopy"] if !ok { t.Fatal("wideCopy not found") } if len(wc.params) != 2 { t.Errorf("wideCopy params: got %d, want 2", len(wc.params)) } if wc.params[0].typ != "[]byte" { t.Errorf("wideCopy param[0].typ = %q, want []byte", wc.params[0].typ) } } func TestFuzzWideCopy(t *testing.T) { k := loadBasic(t) gt, err := GroundTruth("../testdata/verify/basic_amd64.s") if err != nil { t.Fatalf("GroundTruth: %v", err) } goCode, ok := gt["wideCopy"] if !ok { t.Skip("wideCopy not in ground truth") } sig := funcSig{ name: "wideCopy", params: []param{ {name: "dst", typ: "[]byte"}, {name: "src", typ: "[]byte"}, }, } res := k.FuzzFunc("wideCopy", sig, goCode, 200, 42) if !res.OK() { t.Errorf("wideCopy fuzz: %s", res) } } // TestFuzzFuncSigWiderThanFrame pins the guard against a // func comment // that declares more parameter bytes than the TEXT frame carries: before // the guard, slicing the result area at paramsSize(sig) past the end of // the argument block panicked the whole test binary. func TestFuzzFuncSigWiderThanFrame(t *testing.T) { k := loadBasic(t) gt, err := GroundTruth("../testdata/verify/basic_amd64.s") if err != nil { t.Skipf("go tool asm unavailable: %v", err) } goCode, ok := gt["add"] if !ok { t.Skip("add not in ground truth") } // add carries $0-24; four int parameters declare 32 bytes. sig := funcSig{ name: "add", params: []param{ {name: "a", typ: "int"}, {name: "b", typ: "int"}, {name: "c", typ: "int"}, {name: "d", typ: "int"}, }, } res := k.FuzzFunc("add", sig, goCode, 3, 42) if res.OK() { t.Fatal("expected the over-wide signature to fail the campaign") } if !strings.Contains(res.FirstFail, "parameter bytes") || !strings.Contains(res.FirstFail, "argument bytes") { t.Errorf("FirstFail = %q, want a clear signature-versus-frame message", res.FirstFail) } } // TestFuzzStringParam runs the differential fuzzer over a kernel whose // only parameter is a string: the marshaller lays out a real two-word // header (data pointer + length) and the comparison slices at // paramsSize(sig) = 16, so the length word is input, not result. func TestFuzzStringParam(t *testing.T) { k := loadStrProbeKernel(t) file := t.TempDir() + "/strprobe_amd64.s" if err := os.WriteFile(file, []byte(strProbeSrc), 0o644); err != nil { t.Fatalf("write kernel: %v", err) } gt, err := GroundTruth(file) if err != nil { t.Skipf("go tool asm unavailable: %v", err) } goCode, ok := gt["strProbe"] if !ok { t.Skip("strProbe not in ground truth") } sig, ok := parseFuncSig("// func strProbe(s string) int64") if !ok { t.Fatal("parseFuncSig failed") } res := k.FuzzFunc("strProbe", sig, goCode, 100, 42) if !res.OK() { t.Errorf("strProbe fuzz: %s", res) } } func TestParseFuncSig(t *testing.T) { tests := []struct { comment string name string nParams int }{ {"// func add(a int64, b int64) int64", "add", 2}, {"// func wideCopy(dst []byte, src []byte)", "wideCopy", 2}, {"// func analyzeO1RangeAVX2(swin []int32, dstP []uint32, hist *[32]uint16) (partSum uint64, overflow bool)", "analyzeO1RangeAVX2", 3}, {"// not a func", "", 0}, } for _, tt := range tests { sig, ok := parseFuncSig(tt.comment) if tt.name == "" { if ok { t.Errorf("parseFuncSig(%q): expected not ok", tt.comment) } continue } if !ok { t.Errorf("parseFuncSig(%q): expected ok", tt.comment) continue } if sig.name != tt.name { t.Errorf("parseFuncSig(%q).name = %q, want %q", tt.comment, sig.name, tt.name) } if len(sig.params) != tt.nParams { t.Errorf("parseFuncSig(%q): %d params, want %d", tt.comment, len(sig.params), tt.nParams) } } }