123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- // Copyright 2016 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package bpf
- import (
- "errors"
- "fmt"
- )
- // A VM is an emulated BPF virtual machine.
- type VM struct {
- filter []Instruction
- }
- // NewVM returns a new VM using the input BPF program.
- func NewVM(filter []Instruction) (*VM, error) {
- if len(filter) == 0 {
- return nil, errors.New("one or more Instructions must be specified")
- }
- for i, ins := range filter {
- check := len(filter) - (i + 1)
- switch ins := ins.(type) {
- // Check for out-of-bounds jumps in instructions
- case Jump:
- if check <= int(ins.Skip) {
- return nil, fmt.Errorf("cannot jump %d instructions; jumping past program bounds", ins.Skip)
- }
- case JumpIf:
- if check <= int(ins.SkipTrue) {
- return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
- }
- if check <= int(ins.SkipFalse) {
- return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
- }
- case JumpIfX:
- if check <= int(ins.SkipTrue) {
- return nil, fmt.Errorf("cannot jump %d instructions in true case; jumping past program bounds", ins.SkipTrue)
- }
- if check <= int(ins.SkipFalse) {
- return nil, fmt.Errorf("cannot jump %d instructions in false case; jumping past program bounds", ins.SkipFalse)
- }
- // Check for division or modulus by zero
- case ALUOpConstant:
- if ins.Val != 0 {
- break
- }
- switch ins.Op {
- case ALUOpDiv, ALUOpMod:
- return nil, errors.New("cannot divide by zero using ALUOpConstant")
- }
- // Check for unknown extensions
- case LoadExtension:
- switch ins.Num {
- case ExtLen:
- default:
- return nil, fmt.Errorf("extension %d not implemented", ins.Num)
- }
- }
- }
- // Make sure last instruction is a return instruction
- switch filter[len(filter)-1].(type) {
- case RetA, RetConstant:
- default:
- return nil, errors.New("BPF program must end with RetA or RetConstant")
- }
- // Though our VM works using disassembled instructions, we
- // attempt to assemble the input filter anyway to ensure it is compatible
- // with an operating system VM.
- _, err := Assemble(filter)
- return &VM{
- filter: filter,
- }, err
- }
- // Run runs the VM's BPF program against the input bytes.
- // Run returns the number of bytes accepted by the BPF program, and any errors
- // which occurred while processing the program.
- func (v *VM) Run(in []byte) (int, error) {
- var (
- // Registers of the virtual machine
- regA uint32
- regX uint32
- regScratch [16]uint32
- // OK is true if the program should continue processing the next
- // instruction, or false if not, causing the loop to break
- ok = true
- )
- // TODO(mdlayher): implement:
- // - NegateA:
- // - would require a change from uint32 registers to int32
- // registers
- // TODO(mdlayher): add interop tests that check signedness of ALU
- // operations against kernel implementation, and make sure Go
- // implementation matches behavior
- for i := 0; i < len(v.filter) && ok; i++ {
- ins := v.filter[i]
- switch ins := ins.(type) {
- case ALUOpConstant:
- regA = aluOpConstant(ins, regA)
- case ALUOpX:
- regA, ok = aluOpX(ins, regA, regX)
- case Jump:
- i += int(ins.Skip)
- case JumpIf:
- jump := jumpIf(ins, regA)
- i += jump
- case JumpIfX:
- jump := jumpIfX(ins, regA, regX)
- i += jump
- case LoadAbsolute:
- regA, ok = loadAbsolute(ins, in)
- case LoadConstant:
- regA, regX = loadConstant(ins, regA, regX)
- case LoadExtension:
- regA = loadExtension(ins, in)
- case LoadIndirect:
- regA, ok = loadIndirect(ins, in, regX)
- case LoadMemShift:
- regX, ok = loadMemShift(ins, in)
- case LoadScratch:
- regA, regX = loadScratch(ins, regScratch, regA, regX)
- case RetA:
- return int(regA), nil
- case RetConstant:
- return int(ins.Val), nil
- case StoreScratch:
- regScratch = storeScratch(ins, regScratch, regA, regX)
- case TAX:
- regX = regA
- case TXA:
- regA = regX
- default:
- return 0, fmt.Errorf("unknown Instruction at index %d: %T", i, ins)
- }
- }
- return 0, nil
- }
|