Berkeley Packet Filter (BPF) assembler.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.7KB

  1. package bpf
  2. import (
  3. "errors"
  4. "fmt"
  5. "golang.org/x/net/bpf"
  6. )
  7. // A VM is an emulated BPF virtual machine.
  8. type VM struct {
  9. // Extensions are optional callback to load extensions. Note that the
  10. // "len" extension is always provided by the VM.
  11. Extensions map[bpf.Extension]func(*Registers) uint32
  12. program Program
  13. }
  14. func NewVM(program Program) *VM {
  15. return &VM{
  16. program: program,
  17. }
  18. }
  19. // Verify runs sanity checks on the loaded program.
  20. func (vm *VM) Verify() error {
  21. if len(vm.program) == 0 {
  22. return errors.New("bpf: no instructions in program")
  23. }
  24. for i, ins := range vm.program {
  25. check := len(vm.program) - (i + 1)
  26. switch ins := ins.(type) {
  27. // Check for out-of-bounds jumps in instructions
  28. case bpf.Jump:
  29. if check <= int(ins.Skip) {
  30. return fmt.Errorf("bpf: cannot jump %d instructions; jumping past program bounds", ins.Skip)
  31. }
  32. case bpf.JumpIf:
  33. if check <= int(ins.SkipTrue) {
  34. return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
  35. }
  36. if check <= int(ins.SkipFalse) {
  37. return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
  38. }
  39. case bpf.JumpIfX:
  40. if check <= int(ins.SkipTrue) {
  41. return fmt.Errorf("bpf: cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
  42. }
  43. if check <= int(ins.SkipFalse) {
  44. return fmt.Errorf("bpf: cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
  45. }
  46. // Check for division or modulus by zero
  47. case bpf.ALUOpConstant:
  48. if ins.Val != 0 {
  49. break
  50. }
  51. switch ins.Op {
  52. case bpf.ALUOpDiv, bpf.ALUOpMod:
  53. return errors.New("cannot divide by zero using ALUOpConstant")
  54. }
  55. // Check for unknown extensions
  56. case bpf.LoadExtension:
  57. switch ins.Num {
  58. case bpf.ExtLen:
  59. default:
  60. if vm.Extensions != nil {
  61. if _, ok := vm.Extensions[ins.Num]; ok {
  62. continue
  63. }
  64. }
  65. return fmt.Errorf("extension %d not implemented", ins.Num)
  66. }
  67. }
  68. }
  69. // Make sure last instruction is a return instruction
  70. switch vm.program[len(vm.program)-1].(type) {
  71. case bpf.RetA, bpf.RetConstant:
  72. default:
  73. return errors.New("bpf: program must end with RetA or RetConstant")
  74. }
  75. // Though our VM works using disassembled instructions, we
  76. // attempt to assemble the input filter anyway to ensure it is compatible
  77. // with an operating system VM.
  78. _, err := bpf.Assemble(vm.program)
  79. return err
  80. }
  81. func (vm *VM) Run(in []byte) (verdict uint32, err error) {
  82. var (
  83. reg = new(Registers)
  84. end = uint32(len(vm.program))
  85. ok = true
  86. )
  87. for ok && reg.PC < end {
  88. debugf("ins=%s (%T) reg=%+v -> ", vm.program[reg.PC], vm.program[reg.PC], reg)
  89. switch ins := vm.program[reg.PC].(type) {
  90. case bpf.ALUOpConstant:
  91. reg.aluOpConstant(ins)
  92. case bpf.ALUOpX:
  93. ok = reg.aluOpX(ins)
  94. case bpf.Jump:
  95. reg.PC += uint32(ins.Skip)
  96. case bpf.JumpIf:
  97. reg.jumpIf(ins)
  98. case bpf.JumpIfX:
  99. reg.jumpIfX(ins)
  100. case bpf.LoadAbsolute:
  101. ok = reg.loadAbsolute(ins, in)
  102. case bpf.LoadConstant:
  103. reg.loadConstant(ins)
  104. case bpf.LoadExtension:
  105. reg.loadExtension(ins, in, vm.Extensions)
  106. case bpf.LoadIndirect:
  107. ok = reg.loadIndirect(ins, in)
  108. case bpf.LoadMemShift:
  109. ok = reg.loadMemShift(ins, in)
  110. case bpf.LoadScratch:
  111. reg.loadScratch(ins)
  112. case bpf.RetA:
  113. return reg.A, nil
  114. case bpf.RetConstant:
  115. return ins.Val, nil
  116. case bpf.StoreScratch:
  117. reg.storeScratch(ins)
  118. case bpf.TAX:
  119. reg.X = reg.A
  120. case bpf.TXA:
  121. reg.A = reg.X
  122. case bpf.NegateA:
  123. reg.A = uint32(-int32(reg.A))
  124. default:
  125. return 0, fmt.Errorf("bpf: unknown instruction at pc=%d: %T", reg.PC-1, ins)
  126. }
  127. debugf("reg=%+v\n", reg)
  128. reg.PC++
  129. }
  130. return
  131. }