gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/bpf/bpf.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package bpf provides tools for working with Berkeley Packet Filter (BPF)
    16  // programs. More information on BPF can be found at
    17  // https://www.freebsd.org/cgi/man.cgi?bpf(4)
    18  package bpf
    19  
    20  import (
    21  	"fmt"
    22  
    23  	"gvisor.dev/gvisor/pkg/abi/linux"
    24  )
    25  
    26  const (
    27  	// MaxInstructions is the maximum number of instructions in a BPF program,
    28  	// and is equal to Linux's BPF_MAXINSNS.
    29  	MaxInstructions = 4096
    30  
    31  	// ScratchMemRegisters is the number of M registers in a BPF virtual machine,
    32  	// and is equal to Linux's BPF_MEMWORDS.
    33  	ScratchMemRegisters = 16
    34  )
    35  
    36  // Parts of a linux.BPFInstruction.OpCode. Compare to the Linux kernel's
    37  // include/uapi/linux/filter.h.
    38  //
    39  // In the comments below:
    40  //
    41  //   - A, X, and M[] are BPF virtual machine registers.
    42  //
    43  //   - K refers to the instruction field linux.BPFInstruction.K.
    44  //
    45  //   - Bits are counted from the LSB position.
    46  const (
    47  	// Instruction class, stored in bits 0-2.
    48  	Ld                   = 0x00 // load into A
    49  	Ldx                  = 0x01 // load into X
    50  	St                   = 0x02 // store from A
    51  	Stx                  = 0x03 // store from X
    52  	Alu                  = 0x04 // arithmetic
    53  	Jmp                  = 0x05 // jump
    54  	Ret                  = 0x06 // return
    55  	Misc                 = 0x07
    56  	instructionClassMask = 0x07
    57  
    58  	// Size of a load, stored in bits 3-4.
    59  	W            = 0x00 // 32 bits
    60  	H            = 0x08 // 16 bits
    61  	B            = 0x10 // 8 bits
    62  	loadSizeMask = 0x18
    63  
    64  	// Source operand for a load, stored in bits 5-7.
    65  	// Address mode numbers in the comments come from Linux's
    66  	// Documentation/networking/filter.txt.
    67  	Imm          = 0x00 // immediate value K (mode 4)
    68  	Abs          = 0x20 // data in input at byte offset K (mode 1)
    69  	Ind          = 0x40 // data in input at byte offset X+K (mode 2)
    70  	Mem          = 0x60 // M[K] (mode 3)
    71  	Len          = 0x80 // length of the input in bytes ("BPF extension len")
    72  	Msh          = 0xa0 // 4 * lower nibble of input at byte offset K (mode 5)
    73  	loadModeMask = 0xe0
    74  
    75  	// Source operands for arithmetic, jump, and return instructions.
    76  	// Arithmetic and jump instructions can use K or X as source operands.
    77  	// Return instructions can use K or A as source operands.
    78  	K             = 0x00 // still mode 4
    79  	X             = 0x08 // mode 0
    80  	A             = 0x10 // mode 9
    81  	operandMask   = K | X | A
    82  	srcAluJmpMask = 0x08
    83  	srcRetMask    = 0x18
    84  
    85  	// Arithmetic instructions, stored in bits 4-7.
    86  	Add     = 0x00
    87  	Sub     = 0x10 // A - src
    88  	Mul     = 0x20
    89  	Div     = 0x30 // A / src
    90  	Or      = 0x40
    91  	And     = 0x50
    92  	Lsh     = 0x60 // A << src
    93  	Rsh     = 0x70 // A >> src
    94  	Neg     = 0x80 // -A (src ignored)
    95  	Mod     = 0x90 // A % src
    96  	Xor     = 0xa0
    97  	aluMask = 0xf0
    98  
    99  	// Jump instructions, stored in bits 4-7.
   100  	Ja      = 0x00 // unconditional (uses K for jump offset)
   101  	Jeq     = 0x10 // if A == src
   102  	Jgt     = 0x20 // if A > src
   103  	Jge     = 0x30 // if A >= src
   104  	Jset    = 0x40 // if (A & src) != 0
   105  	jmpMask = 0xf0
   106  
   107  	// Miscellaneous instructions, stored in bits 3-7.
   108  	Tax      = 0x00 // A = X
   109  	Txa      = 0x80 // X = A
   110  	miscMask = 0xf8
   111  
   112  	// Masks for bits that should be zero.
   113  	unusedBitsMask      = 0xff00 // all valid instructions use only bits 0-7
   114  	storeUnusedBitsMask = 0xf8   // stores only use instruction class
   115  	retUnusedBitsMask   = 0xe0   // returns only use instruction class and source operand
   116  )
   117  
   118  // Instruction is a type alias for linux.BPFInstruction.
   119  // It adds a human-readable stringification and other helper functions.
   120  //
   121  // +marshal slice:InstructionSlice
   122  // +stateify savable
   123  // +stateify identtype
   124  type Instruction linux.BPFInstruction
   125  
   126  // String returns a human-readable version of the instruction.
   127  func (ins *Instruction) String() string {
   128  	s, err := Decode(*ins)
   129  	if err != nil {
   130  		return fmt.Sprintf("[invalid %v: %v]", (*linux.BPFInstruction)(ins), err)
   131  	}
   132  	return s
   133  }
   134  
   135  // Stmt returns an Instruction representing a BPF non-jump instruction.
   136  func Stmt(code uint16, k uint32) Instruction {
   137  	return Instruction{
   138  		OpCode: code,
   139  		K:      k,
   140  	}
   141  }
   142  
   143  // Jump returns an Instruction representing a BPF jump instruction.
   144  func Jump(code uint16, k uint32, jt, jf uint8) Instruction {
   145  	return Instruction{
   146  		OpCode:      code,
   147  		JumpIfTrue:  jt,
   148  		JumpIfFalse: jf,
   149  		K:           k,
   150  	}
   151  }
   152  
   153  // Equal returns whether this instruction is equivalent to `other`.
   154  func (ins Instruction) Equal(other Instruction) bool {
   155  	if ins.OpCode != other.OpCode {
   156  		// If instructions don't have the same opcode, they are not equal.
   157  		return false
   158  	}
   159  	switch ins.OpCode & instructionClassMask {
   160  	case Ld, Ldx:
   161  		if ins.OpCode&loadModeMask == Len {
   162  			// Length instructions are independent of the K register.
   163  			return true
   164  		}
   165  		// Two load instructions are the same if they load from the same offset.
   166  		return ins.K == other.K
   167  	case St, Stx:
   168  		// Two store instructions are the same if they store at the same offset.
   169  		return ins.K == other.K
   170  	case Alu:
   171  		if ins.OpCode == Alu|Neg {
   172  			return true // The negation instruction has no operands.
   173  		}
   174  		if ins.OpCode&operandMask == X {
   175  			// If we use X, no need to check anything.
   176  			return true
   177  		}
   178  		if ins.OpCode&operandMask == K {
   179  			// If use K, check that it's the same.
   180  			return ins.K == other.K
   181  		}
   182  		// Otherwise, we use the whole instruction.
   183  	case Ret:
   184  		switch ins.OpCode {
   185  		case Ret | A:
   186  			// All instructions that return the A register are equivalent.
   187  			return true
   188  		case Ret | K:
   189  			// All instructions that return the same value are equivalent.
   190  			return ins.K == other.K
   191  		}
   192  	case Jmp:
   193  		if ins.IsUnconditionalJump() {
   194  			// Unconditional jumps to the same offset are equivalent.
   195  			return ins.K == other.K
   196  		}
   197  		if ins.OpCode&operandMask == X {
   198  			// If we use X as the operand, check the conditional jump targets only.
   199  			return ins.JumpIfTrue == other.JumpIfTrue && ins.JumpIfFalse == other.JumpIfFalse
   200  		}
   201  		// Otherwise, we use the whole instruction.
   202  	case Misc:
   203  		if ins.OpCode == Misc|Tax || ins.OpCode == Misc|Txa {
   204  			// Swapping X and A, we don't care about the other fields.
   205  			return true
   206  		}
   207  	}
   208  	// All other instructions need full bit-for-bit comparison.
   209  	return ins == other
   210  }
   211  
   212  // IsReturn returns true if `ins` is a return instruction.
   213  func (ins Instruction) IsReturn() bool {
   214  	return ins.OpCode&instructionClassMask == Ret
   215  }
   216  
   217  // IsJump returns true if `ins` is a jump instruction.
   218  func (ins Instruction) IsJump() bool {
   219  	return ins.OpCode&instructionClassMask == Jmp
   220  }
   221  
   222  // IsConditionalJump returns true if `ins` is a conditional jump instruction.
   223  func (ins Instruction) IsConditionalJump() bool {
   224  	return ins.IsJump() && ins.OpCode&jmpMask != Ja
   225  }
   226  
   227  // IsUnconditionalJump returns true if `ins` is a conditional jump instruction.
   228  func (ins Instruction) IsUnconditionalJump() bool {
   229  	return ins.IsJump() && ins.OpCode&jmpMask == Ja
   230  }
   231  
   232  // JumpOffset is a possible jump offset that an instruction may jump to.
   233  type JumpOffset struct {
   234  	// Type is the type of jump that an instruction may execute.
   235  	Type JumpType
   236  
   237  	// Offset is the number of instructions that the jump skips over.
   238  	Offset uint32
   239  }
   240  
   241  // JumpOffsets returns the set of instruction offsets that this instruction
   242  // may jump to. Returns a nil slice if this is not a jump instruction.
   243  func (ins Instruction) JumpOffsets() []JumpOffset {
   244  	if !ins.IsJump() {
   245  		return nil
   246  	}
   247  	if ins.IsConditionalJump() {
   248  		return []JumpOffset{
   249  			{JumpTrue, uint32(ins.JumpIfTrue)},
   250  			{JumpFalse, uint32(ins.JumpIfFalse)},
   251  		}
   252  	}
   253  	return []JumpOffset{{JumpDirect, ins.K}}
   254  }
   255  
   256  // ModifiesRegisterA returns true iff this instruction modifies the value
   257  // of the "A" register.
   258  func (ins Instruction) ModifiesRegisterA() bool {
   259  	switch ins.OpCode & instructionClassMask {
   260  	case Ld:
   261  		return true
   262  	case Alu:
   263  		return true
   264  	case Misc:
   265  		return ins.OpCode == Misc|Tax
   266  	default:
   267  		return false
   268  	}
   269  }