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.
 
 
 

151 lines
4.0 KiB

  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. case JumpIfX:
  34. if check <= int(ins.SkipTrue) {
  35. return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
  36. }
  37. if check <= int(ins.SkipFalse) {
  38. return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
  39. }
  40. // Check for division or modulus by zero
  41. case ALUOpConstant:
  42. if ins.Val != 0 {
  43. break
  44. }
  45. switch ins.Op {
  46. case ALUOpDiv, ALUOpMod:
  47. return nil, errors.New("cannot divide by zero using ALUOpConstant")
  48. }
  49. // Check for unknown extensions
  50. case LoadExtension:
  51. switch ins.Num {
  52. case ExtLen:
  53. default:
  54. return nil, fmt.Errorf("extension %d not implemented", ins.Num)
  55. }
  56. }
  57. }
  58. // Make sure last instruction is a return instruction
  59. switch filter[len(filter)-1].(type) {
  60. case RetA, RetConstant:
  61. default:
  62. return nil, errors.New("BPF program must end with RetA or RetConstant")
  63. }
  64. // Though our VM works using disassembled instructions, we
  65. // attempt to assemble the input filter anyway to ensure it is compatible
  66. // with an operating system VM.
  67. _, err := Assemble(filter)
  68. return &VM{
  69. filter: filter,
  70. }, err
  71. }
  72. // Run runs the VM's BPF program against the input bytes.
  73. // Run returns the number of bytes accepted by the BPF program, and any errors
  74. // which occurred while processing the program.
  75. func (v *VM) Run(in []byte) (int, error) {
  76. var (
  77. // Registers of the virtual machine
  78. regA uint32
  79. regX uint32
  80. regScratch [16]uint32
  81. // OK is true if the program should continue processing the next
  82. // instruction, or false if not, causing the loop to break
  83. ok = true
  84. )
  85. // TODO(mdlayher): implement:
  86. // - NegateA:
  87. // - would require a change from uint32 registers to int32
  88. // registers
  89. // TODO(mdlayher): add interop tests that check signedness of ALU
  90. // operations against kernel implementation, and make sure Go
  91. // implementation matches behavior
  92. for i := 0; i < len(v.filter) && ok; i++ {
  93. ins := v.filter[i]
  94. switch ins := ins.(type) {
  95. case ALUOpConstant:
  96. regA = aluOpConstant(ins, regA)
  97. case ALUOpX:
  98. regA, ok = aluOpX(ins, regA, regX)
  99. case Jump:
  100. i += int(ins.Skip)
  101. case JumpIf:
  102. jump := jumpIf(ins, regA)
  103. i += jump
  104. case JumpIfX:
  105. jump := jumpIfX(ins, regA, regX)
  106. i += jump
  107. case LoadAbsolute:
  108. regA, ok = loadAbsolute(ins, in)
  109. case LoadConstant:
  110. regA, regX = loadConstant(ins, regA, regX)
  111. case LoadExtension:
  112. regA = loadExtension(ins, in)
  113. case LoadIndirect:
  114. regA, ok = loadIndirect(ins, in, regX)
  115. case LoadMemShift:
  116. regX, ok = loadMemShift(ins, in)
  117. case LoadScratch:
  118. regA, regX = loadScratch(ins, regScratch, regA, regX)
  119. case RetA:
  120. return int(regA), nil
  121. case RetConstant:
  122. return int(ins.Val), nil
  123. case StoreScratch:
  124. regScratch = storeScratch(ins, regScratch, regA, regX)
  125. case TAX:
  126. regX = regA
  127. case TXA:
  128. regA = regX
  129. default:
  130. return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
  131. }
  132. }
  133. return 0, nil
  134. }