vm.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Copyright 2016 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package bpf
  5. import (
  6. "errors"
  7. "fmt"
  8. )
  9. // A VM is an emulated BPF virtual machine.
  10. type VM struct {
  11. filter []Instruction
  12. }
  13. // NewVM returns a new VM using the input BPF program.
  14. func NewVM(filter []Instruction) (*VM, error) {
  15. if len(filter) == 0 {
  16. return nil, errors.New("one or more Instructions must be specified")
  17. }
  18. for i, ins := range filter {
  19. check := len(filter) - (i + 1)
  20. switch ins := ins.(type) {
  21. // Check for out-of-bounds jumps in instructions
  22. case Jump:
  23. if check <= int(ins.Skip) {
  24. return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
  25. }
  26. case JumpIf:
  27. if check <= int(ins.SkipTrue) {
  28. return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
  29. }
  30. if check <= int(ins.SkipFalse) {
  31. return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
  32. }
  33. // Check for division or modulus by zero
  34. case ALUOpConstant:
  35. if ins.Val != 0 {
  36. break
  37. }
  38. switch ins.Op {
  39. case ALUOpDiv, ALUOpMod:
  40. return nil, errors.New("cannot divide by zero using ALUOpConstant")
  41. }
  42. }
  43. }
  44. // Make sure last instruction is a return instruction
  45. switch filter[len(filter)-1].(type) {
  46. case RetA, RetConstant:
  47. default:
  48. return nil, errors.New("BPF program must end with RetA or RetConstant")
  49. }
  50. // Though our VM works using disassembled instructions, we
  51. // attempt to assemble the input filter anyway to ensure it is compatible
  52. // with an operating system VM.
  53. _, err := Assemble(filter)
  54. return &VM{
  55. filter: filter,
  56. }, err
  57. }
  58. // Run runs the VM's BPF program against the input bytes.
  59. // Run returns the number of bytes accepted by the BPF program, and any errors
  60. // which occurred while processing the program.
  61. func (v *VM) Run(in []byte) (int, error) {
  62. var (
  63. // Registers of the virtual machine
  64. regA uint32
  65. regX uint32
  66. regScratch [16]uint32
  67. // OK is true if the program should continue processing the next
  68. // instruction, or false if not, causing the loop to break
  69. ok = true
  70. )
  71. // TODO(mdlayher): implement:
  72. // - NegateA:
  73. // - would require a change from uint32 registers to int32
  74. // registers
  75. // - Extension:
  76. // - implement extensions that do not depend on kernel-specific
  77. // functionality, such as 'rand'
  78. // TODO(mdlayher): add interop tests that check signedness of ALU
  79. // operations against kernel implementation, and make sure Go
  80. // implementation matches behavior
  81. for i := 0; i < len(v.filter) && ok; i++ {
  82. ins := v.filter[i]
  83. switch ins := ins.(type) {
  84. case ALUOpConstant:
  85. regA = aluOpConstant(ins, regA)
  86. case ALUOpX:
  87. regA, ok = aluOpX(ins, regA, regX)
  88. case Jump:
  89. i += int(ins.Skip)
  90. case JumpIf:
  91. jump := jumpIf(ins, regA)
  92. i += jump
  93. case LoadAbsolute:
  94. regA, ok = loadAbsolute(ins, in)
  95. case LoadConstant:
  96. regA, regX = loadConstant(ins, regA, regX)
  97. case LoadIndirect:
  98. regA, ok = loadIndirect(ins, in, regX)
  99. case LoadMemShift:
  100. regX, ok = loadMemShift(ins, in)
  101. case LoadScratch:
  102. regA, regX = loadScratch(ins, regScratch, regA, regX)
  103. case RetA:
  104. return int(regA), nil
  105. case RetConstant:
  106. return int(ins.Val), nil
  107. case StoreScratch:
  108. regScratch = storeScratch(ins, regScratch, regA, regX)
  109. case TAX:
  110. regX = regA
  111. case TXA:
  112. regA = regX
  113. default:
  114. return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
  115. }
  116. }
  117. return 0, nil
  118. }