vm.go raw

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