feat(verify): scalar arguments for -call invocations

This commit is contained in:
2026-08-29 14:03:45 +02:00
parent 6fb9629ab6
commit e44162a749
3 changed files with 136 additions and 6 deletions
+70
View File
@@ -148,3 +148,73 @@ func fillBuffer(buf []byte, pattern string) {
}
}
}
// ApplyScalarArgs writes user-supplied scalar argument values into an ABI0
// argument block after BuildArgs. Each name=value pair addresses the layout
// entry of that parameter name; only word-sized scalar parameters (int,
// int64, uint64, and their named spellings) accept values, so a typo'd name
// or a slice parameter fails loudly instead of silently corrupting the call.
func ApplyScalarArgs(args []byte, layout []ArgOffset, scalars map[string]uint64) error {
if len(scalars) == 0 {
return nil
}
byName := make(map[string]ArgOffset, len(layout))
for _, l := range layout {
byName[l.Name] = l
}
for name, val := range scalars {
l, ok := byName[name]
if !ok {
return fmt.Errorf("scalar arg %q: no such parameter", name)
}
if l.IsPtr || l.Size != 8 {
return fmt.Errorf("scalar arg %q: parameter has type %s; only word-sized scalars accept values", name, l.Typ)
}
if l.Offset+8 > len(args) {
return fmt.Errorf("scalar arg %q: offset %d outside the %d-byte arg block", name, l.Offset, len(args))
}
binary.LittleEndian.PutUint64(args[l.Offset:], val)
}
return nil
}
// ParseScalarArgs parses a "name=value[,name=value...]" spec into a map.
// Values are decimal or 0x-prefixed hex.
func ParseScalarArgs(spec string) (map[string]uint64, error) {
out := map[string]uint64{}
if strings.TrimSpace(spec) == "" {
return out, nil
}
for _, part := range strings.Split(spec, ",") {
part = strings.TrimSpace(part)
if part == "" {
continue
}
name, val, err := splitScalar(part)
if err != nil {
return nil, err
}
out[name] = val
}
return out, nil
}
func splitScalar(part string) (string, uint64, error) {
eq := strings.Index(part, "=")
if eq <= 0 {
return "", 0, fmt.Errorf("scalar arg spec %q: want name=value", part)
}
name := strings.TrimSpace(part[:eq])
valStr := strings.TrimSpace(part[eq+1:])
var val uint64
var err error
if strings.HasPrefix(valStr, "0x") || strings.HasPrefix(valStr, "0X") {
_, err = fmt.Sscanf(valStr, "0x%x", &val)
} else {
_, err = fmt.Sscanf(valStr, "%d", &val)
}
if err != nil || name == "" {
return "", 0, fmt.Errorf("scalar arg spec %q: bad value", part)
}
return name, val, nil
}
+38
View File
@@ -156,3 +156,41 @@ func TestBufPoolBuildArgs(t *testing.T) {
t.Errorf("src.len = %d, want 128", args[32])
}
}
func TestParseScalarArgs(t *testing.T) {
m, err := ParseScalarArgs("n=57,base=0x1f,x= 12 ")
if err != nil {
t.Fatal(err)
}
if m["n"] != 57 || m["base"] != 0x1f || m["x"] != 12 {
t.Fatalf("parsed %+v", m)
}
if _, err := ParseScalarArgs("n"); err == nil {
t.Error("name-only spec must fail")
}
if _, err := ParseScalarArgs("n=zz"); err == nil {
t.Error("bad value must fail")
}
}
func TestApplyScalarArgs(t *testing.T) {
layout := []ArgOffset{
{Name: "text", Typ: "*byte", Offset: 0, Size: 8, IsPtr: true},
{Name: "n", Typ: "int", Offset: 8, Size: 8, IsPtr: false},
}
args := make([]byte, 16)
if err := ApplyScalarArgs(args, layout, map[string]uint64{"n": 0x1f}); err != nil {
t.Fatal(err)
}
if got := args[8]; got != 0x1f {
t.Fatalf("args[8] = %d, want 31", got)
}
// Pointer parameters reject scalar writes.
if err := ApplyScalarArgs(args, layout, map[string]uint64{"text": 5}); err == nil {
t.Fatal("pointer param must reject a scalar write")
}
// Unknown names reject.
if err := ApplyScalarArgs(args, layout, map[string]uint64{"nope": 1}); err == nil {
t.Fatal("unknown param must reject")
}
}