radiance/pkg/sbpf/verifier.go

116 lines
2.9 KiB
Go

package sbpf
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
}