forked from firedancer-io/radiance
/
verifier.go
115 lines (107 loc) · 2.86 KB
/
verifier.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package sbf
import "fmt"
type Verifier struct {
Program *Program
}
func NewVerifier(p *Program) *Verifier {
return &Verifier{Program: p}
}
func (v *Verifier) Verify() error {
text := v.Program.Text
if len(text)%SlotSize != 0 {
return fmt.Errorf("odd .text size")
}
if len(text) == 0 {
return fmt.Errorf("empty text")
}
for pc := uint64(0); (pc+1)*SlotSize <= uint64(len(text)); pc++ {
insBytes := text[pc*SlotSize:]
ins := GetSlot(insBytes)
if ins.Src() > 10 {
return fmt.Errorf("invalid src register")
}
switch ins.Op() {
case OpLdxb, OpLdxh, OpLdxw, OpLdxdw:
case OpAdd32Imm, OpAdd32Reg, OpAdd64Imm, OpAdd64Reg:
case OpSub32Imm, OpSub32Reg, OpSub64Imm, OpSub64Reg:
case OpMul32Imm, OpMul32Reg, OpMul64Imm, OpMul64Reg:
case OpOr32Imm, OpOr32Reg, OpOr64Imm, OpOr64Reg:
case OpAnd32Imm, OpAnd32Reg, OpAnd64Imm, OpAnd64Reg:
case OpLsh32Reg, OpLsh64Reg:
case OpRsh32Reg, OpRsh64Reg:
case OpNeg32, OpNeg64:
case OpXor32Imm, OpXor32Reg, OpXor64Imm, OpXor64Reg:
case OpMov32Imm, OpMov32Reg, OpMov64Imm, OpMov64Reg:
case OpDiv32Reg, OpDiv64Reg:
case OpMod32Reg, OpMod64Reg:
case OpSdiv32Reg, OpSdiv64Reg:
case OpCall, OpExit:
// nothing
case OpStb, OpSth, OpStw, OpStdw,
OpStxb, OpStxh, OpStxw, OpStxdw:
if ins.Dst() > 10 {
return fmt.Errorf("invalid dst register")
}
continue
case OpLsh32Imm, OpRsh32Imm, OpArsh32Imm:
if ins.Uimm() > 31 {
return fmt.Errorf("32-bit shift out of bounds")
}
case OpLsh64Imm, OpRsh64Imm, OpArsh64Imm:
if ins.Uimm() > 63 {
return fmt.Errorf("64-bit shift out of bounds")
}
case OpLe, OpBe:
switch ins.Uimm() {
case 16, 32, 64:
// ok
default:
return fmt.Errorf("invalid bit size for endianness conversion")
}
case OpSdiv32Imm, OpSdiv64Imm:
fallthrough
case OpDiv32Imm, OpDiv64Imm, OpMod32Imm, OpMod64Imm:
if ins.Imm() == 0 {
return ExcDivideByZero
}
case OpJa,
OpJeqImm, OpJeqReg,
OpJgtImm, OpJgtReg,
OpJgeImm, OpJgeReg,
OpJltImm, OpJltReg,
OpJleImm, OpJleReg,
OpJsetImm, OpJsetReg,
OpJneImm, OpJneReg,
OpJsgtImm, OpJsgtReg,
OpJsgeImm, OpJsgeReg,
OpJsltImm, OpJsltReg,
OpJsleImm, OpJsleReg:
dst := int64(pc) + int64(ins.Off()) + 1
if dst < 0 || (dst*SlotSize) >= int64(len(text)) {
return fmt.Errorf("jump out of code")
}
dstIns := GetSlot(text[dst*SlotSize:])
if dstIns.Op() == 0 {
return fmt.Errorf("jump into middle of instruction")
}
case OpCallx:
if uimm := ins.Uimm(); uimm >= 10 {
return fmt.Errorf("invalid callx register")
}
case OpLddw:
if len(insBytes) < 2*SlotSize {
return fmt.Errorf("incomplete lddw instruction")
}
if insBytes[8] != 0 {
return fmt.Errorf("malformed lddw instruction")
}
pc++
default:
return fmt.Errorf("unknown opcode %#02x", ins.Op())
}
if ins.Dst() > 9 {
return fmt.Errorf("invalid dst register")
}
}
return nil
}