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.

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