mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:32:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			150 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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
 | |
| }
 |