114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
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 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
|
|
}
|