gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/bpf/interpreter.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
    16  
    17  import (
    18  	"fmt"
    19  	"strconv"
    20  	"strings"
    21  )
    22  
    23  // Possible values for ProgramError.Code.
    24  const (
    25  	// DivisionByZero indicates that a program contains, or executed, a
    26  	// division or modulo by zero.
    27  	DivisionByZero = iota
    28  
    29  	// InvalidEndOfProgram indicates that the last instruction of a program is
    30  	// not a return.
    31  	InvalidEndOfProgram
    32  
    33  	// InvalidInstructionCount indicates that a program has zero instructions
    34  	// or more than MaxInstructions instructions.
    35  	InvalidInstructionCount
    36  
    37  	// InvalidJumpTarget indicates that a program contains a jump whose target
    38  	// is outside of the program's bounds.
    39  	InvalidJumpTarget
    40  
    41  	// InvalidLoad indicates that a program executed an invalid load of input
    42  	// data.
    43  	InvalidLoad
    44  
    45  	// InvalidOpcode indicates that a program contains an instruction with an
    46  	// invalid opcode.
    47  	InvalidOpcode
    48  
    49  	// InvalidRegister indicates that a program contains a load from, or store
    50  	// to, a non-existent M register (index >= ScratchMemRegisters).
    51  	InvalidRegister
    52  )
    53  
    54  // Error is an error encountered while compiling or executing a BPF program.
    55  type Error struct {
    56  	// Code indicates the kind of error that occurred.
    57  	Code int
    58  
    59  	// PC is the program counter (index into the list of instructions) at which
    60  	// the error occurred.
    61  	PC int
    62  }
    63  
    64  func (e Error) codeString() string {
    65  	switch e.Code {
    66  	case DivisionByZero:
    67  		return "division by zero"
    68  	case InvalidEndOfProgram:
    69  		return "last instruction must be a return"
    70  	case InvalidInstructionCount:
    71  		return "invalid number of instructions"
    72  	case InvalidJumpTarget:
    73  		return "jump target out of bounds"
    74  	case InvalidLoad:
    75  		return "load out of bounds or violates input alignment requirements"
    76  	case InvalidOpcode:
    77  		return "invalid instruction opcode"
    78  	case InvalidRegister:
    79  		return "invalid M register"
    80  	default:
    81  		return "unknown error"
    82  	}
    83  }
    84  
    85  // Error implements error.Error.
    86  func (e Error) Error() string {
    87  	return fmt.Sprintf("at l%d: %s", e.PC, e.codeString())
    88  }
    89  
    90  // Program is a BPF program that has been validated for consistency.
    91  //
    92  // +stateify savable
    93  type Program struct {
    94  	instructions []Instruction
    95  }
    96  
    97  // Length returns the number of instructions in the program.
    98  func (p Program) Length() int {
    99  	return len(p.instructions)
   100  }
   101  
   102  // Compile performs validation and optimization on a sequence of BPF
   103  // instructions before wrapping them in a Program.
   104  func Compile(insns []Instruction, optimize bool) (Program, error) {
   105  	if len(insns) == 0 || len(insns) > MaxInstructions {
   106  		return Program{}, Error{InvalidInstructionCount, len(insns)}
   107  	}
   108  
   109  	// The last instruction must be a return.
   110  	if last := insns[len(insns)-1]; last.OpCode != (Ret|K) && last.OpCode != (Ret|A) {
   111  		return Program{}, Error{InvalidEndOfProgram, len(insns) - 1}
   112  	}
   113  
   114  	// Validate each instruction. Note that we skip a validation Linux does:
   115  	// Linux additionally verifies that every load from an M register is
   116  	// preceded, in every path, by a store to the same M register, in order to
   117  	// avoid having to clear M between programs
   118  	// (net/core/filter.c:check_load_and_stores). We always start with a zeroed
   119  	// M array.
   120  	for pc, i := range insns {
   121  		if i.OpCode&unusedBitsMask != 0 {
   122  			return Program{}, Error{InvalidOpcode, pc}
   123  		}
   124  		switch i.OpCode & instructionClassMask {
   125  		case Ld:
   126  			mode := i.OpCode & loadModeMask
   127  			switch i.OpCode & loadSizeMask {
   128  			case W:
   129  				if mode != Imm && mode != Abs && mode != Ind && mode != Mem && mode != Len {
   130  					return Program{}, Error{InvalidOpcode, pc}
   131  				}
   132  				if mode == Mem && i.K >= ScratchMemRegisters {
   133  					return Program{}, Error{InvalidRegister, pc}
   134  				}
   135  			case H, B:
   136  				if mode != Abs && mode != Ind {
   137  					return Program{}, Error{InvalidOpcode, pc}
   138  				}
   139  			default:
   140  				return Program{}, Error{InvalidOpcode, pc}
   141  			}
   142  		case Ldx:
   143  			mode := i.OpCode & loadModeMask
   144  			switch i.OpCode & loadSizeMask {
   145  			case W:
   146  				if mode != Imm && mode != Mem && mode != Len {
   147  					return Program{}, Error{InvalidOpcode, pc}
   148  				}
   149  				if mode == Mem && i.K >= ScratchMemRegisters {
   150  					return Program{}, Error{InvalidRegister, pc}
   151  				}
   152  			case B:
   153  				if mode != Msh {
   154  					return Program{}, Error{InvalidOpcode, pc}
   155  				}
   156  			default:
   157  				return Program{}, Error{InvalidOpcode, pc}
   158  			}
   159  		case St, Stx:
   160  			if i.OpCode&storeUnusedBitsMask != 0 {
   161  				return Program{}, Error{InvalidOpcode, pc}
   162  			}
   163  			if i.K >= ScratchMemRegisters {
   164  				return Program{}, Error{InvalidRegister, pc}
   165  			}
   166  		case Alu:
   167  			switch i.OpCode & aluMask {
   168  			case Add, Sub, Mul, Or, And, Lsh, Rsh, Xor:
   169  				break
   170  			case Div, Mod:
   171  				if src := i.OpCode & srcAluJmpMask; src == K && i.K == 0 {
   172  					return Program{}, Error{DivisionByZero, pc}
   173  				}
   174  			case Neg:
   175  				// Negation doesn't take a source operand.
   176  				if i.OpCode&srcAluJmpMask != 0 {
   177  					return Program{}, Error{InvalidOpcode, pc}
   178  				}
   179  			default:
   180  				return Program{}, Error{InvalidOpcode, pc}
   181  			}
   182  		case Jmp:
   183  			switch i.OpCode & jmpMask {
   184  			case Ja:
   185  				// Unconditional jump doesn't take a source operand.
   186  				if i.OpCode&srcAluJmpMask != 0 {
   187  					return Program{}, Error{InvalidOpcode, pc}
   188  				}
   189  				// Do the comparison in 64 bits to avoid the possibility of
   190  				// overflow from a very large i.K.
   191  				if uint64(pc)+uint64(i.K)+1 >= uint64(len(insns)) {
   192  					return Program{}, Error{InvalidJumpTarget, pc}
   193  				}
   194  			case Jeq, Jgt, Jge, Jset:
   195  				// jt and jf are uint16s, so there's no threat of overflow.
   196  				if pc+int(i.JumpIfTrue)+1 >= len(insns) {
   197  					return Program{}, Error{InvalidJumpTarget, pc}
   198  				}
   199  				if pc+int(i.JumpIfFalse)+1 >= len(insns) {
   200  					return Program{}, Error{InvalidJumpTarget, pc}
   201  				}
   202  			default:
   203  				return Program{}, Error{InvalidOpcode, pc}
   204  			}
   205  		case Ret:
   206  			if i.OpCode&retUnusedBitsMask != 0 {
   207  				return Program{}, Error{InvalidOpcode, pc}
   208  			}
   209  			if src := i.OpCode & srcRetMask; src != K && src != A {
   210  				return Program{}, Error{InvalidOpcode, pc}
   211  			}
   212  		case Misc:
   213  			if misc := i.OpCode & miscMask; misc != Tax && misc != Txa {
   214  				return Program{}, Error{InvalidOpcode, pc}
   215  			}
   216  		}
   217  	}
   218  
   219  	if optimize {
   220  		insns = Optimize(insns)
   221  	}
   222  	return Program{insns}, nil
   223  }
   224  
   225  // machine represents the state of a BPF virtual machine.
   226  type machine struct {
   227  	A uint32
   228  	X uint32
   229  	M [ScratchMemRegisters]uint32
   230  }
   231  
   232  func conditionalJumpOffset(insn Instruction, cond bool) int {
   233  	if cond {
   234  		return int(insn.JumpIfTrue)
   235  	}
   236  	return int(insn.JumpIfFalse)
   237  }
   238  
   239  // Exec executes a BPF program over the given input and returns its return
   240  // value.
   241  func Exec[endian Endianness](p Program, in Input) (uint32, error) {
   242  	var m machine
   243  	var pc int
   244  	for ; pc < len(p.instructions); pc++ {
   245  		i := p.instructions[pc]
   246  		switch i.OpCode {
   247  		case Ld | Imm | W:
   248  			m.A = i.K
   249  		case Ld | Abs | W:
   250  			val, ok := load32[endian](in, i.K)
   251  			if !ok {
   252  				return 0, Error{InvalidLoad, pc}
   253  			}
   254  			m.A = val
   255  		case Ld | Abs | H:
   256  			val, ok := load16[endian](in, i.K)
   257  			if !ok {
   258  				return 0, Error{InvalidLoad, pc}
   259  			}
   260  			m.A = uint32(val)
   261  		case Ld | Abs | B:
   262  			val, ok := load8(in, i.K)
   263  			if !ok {
   264  				return 0, Error{InvalidLoad, pc}
   265  			}
   266  			m.A = uint32(val)
   267  		case Ld | Ind | W:
   268  			val, ok := load32[endian](in, m.X+i.K)
   269  			if !ok {
   270  				return 0, Error{InvalidLoad, pc}
   271  			}
   272  			m.A = val
   273  		case Ld | Ind | H:
   274  			val, ok := load16[endian](in, m.X+i.K)
   275  			if !ok {
   276  				return 0, Error{InvalidLoad, pc}
   277  			}
   278  			m.A = uint32(val)
   279  		case Ld | Ind | B:
   280  			val, ok := load8(in, m.X+i.K)
   281  			if !ok {
   282  				return 0, Error{InvalidLoad, pc}
   283  			}
   284  			m.A = uint32(val)
   285  		case Ld | Mem | W:
   286  			m.A = m.M[int(i.K)]
   287  		case Ld | Len | W:
   288  			m.A = uint32(len(in))
   289  		case Ldx | Imm | W:
   290  			m.X = i.K
   291  		case Ldx | Mem | W:
   292  			m.X = m.M[int(i.K)]
   293  		case Ldx | Len | W:
   294  			m.X = uint32(len(in))
   295  		case Ldx | Msh | B:
   296  			val, ok := load8(in, i.K)
   297  			if !ok {
   298  				return 0, Error{InvalidLoad, pc}
   299  			}
   300  			m.X = 4 * uint32(val&0xf)
   301  		case St:
   302  			m.M[int(i.K)] = m.A
   303  		case Stx:
   304  			m.M[int(i.K)] = m.X
   305  		case Alu | Add | K:
   306  			m.A += i.K
   307  		case Alu | Add | X:
   308  			m.A += m.X
   309  		case Alu | Sub | K:
   310  			m.A -= i.K
   311  		case Alu | Sub | X:
   312  			m.A -= m.X
   313  		case Alu | Mul | K:
   314  			m.A *= i.K
   315  		case Alu | Mul | X:
   316  			m.A *= m.X
   317  		case Alu | Div | K:
   318  			// K != 0 already checked by Compile.
   319  			m.A /= i.K
   320  		case Alu | Div | X:
   321  			if m.X == 0 {
   322  				return 0, Error{DivisionByZero, pc}
   323  			}
   324  			m.A /= m.X
   325  		case Alu | Or | K:
   326  			m.A |= i.K
   327  		case Alu | Or | X:
   328  			m.A |= m.X
   329  		case Alu | And | K:
   330  			m.A &= i.K
   331  		case Alu | And | X:
   332  			m.A &= m.X
   333  		case Alu | Lsh | K:
   334  			m.A <<= i.K
   335  		case Alu | Lsh | X:
   336  			m.A <<= m.X
   337  		case Alu | Rsh | K:
   338  			m.A >>= i.K
   339  		case Alu | Rsh | X:
   340  			m.A >>= m.X
   341  		case Alu | Neg:
   342  			m.A = uint32(-int32(m.A))
   343  		case Alu | Mod | K:
   344  			// K != 0 already checked by Compile.
   345  			m.A %= i.K
   346  		case Alu | Mod | X:
   347  			if m.X == 0 {
   348  				return 0, Error{DivisionByZero, pc}
   349  			}
   350  			m.A %= m.X
   351  		case Alu | Xor | K:
   352  			m.A ^= i.K
   353  		case Alu | Xor | X:
   354  			m.A ^= m.X
   355  		case Jmp | Ja:
   356  			pc += int(i.K)
   357  		case Jmp | Jeq | K:
   358  			pc += conditionalJumpOffset(i, m.A == i.K)
   359  		case Jmp | Jeq | X:
   360  			pc += conditionalJumpOffset(i, m.A == m.X)
   361  		case Jmp | Jgt | K:
   362  			pc += conditionalJumpOffset(i, m.A > i.K)
   363  		case Jmp | Jgt | X:
   364  			pc += conditionalJumpOffset(i, m.A > m.X)
   365  		case Jmp | Jge | K:
   366  			pc += conditionalJumpOffset(i, m.A >= i.K)
   367  		case Jmp | Jge | X:
   368  			pc += conditionalJumpOffset(i, m.A >= m.X)
   369  		case Jmp | Jset | K:
   370  			pc += conditionalJumpOffset(i, (m.A&i.K) != 0)
   371  		case Jmp | Jset | X:
   372  			pc += conditionalJumpOffset(i, (m.A&m.X) != 0)
   373  		case Ret | K:
   374  			return i.K, nil
   375  		case Ret | A:
   376  			return m.A, nil
   377  		case Misc | Tax:
   378  			m.A = m.X
   379  		case Misc | Txa:
   380  			m.X = m.A
   381  		default:
   382  			return 0, Error{InvalidOpcode, pc}
   383  		}
   384  	}
   385  	return 0, Error{InvalidEndOfProgram, pc}
   386  }
   387  
   388  // ExecutionMetrics represents the result of executing a BPF program.
   389  type ExecutionMetrics struct {
   390  	// ReturnValue is the result of the program execution.
   391  	ReturnValue uint32
   392  
   393  	// Coverage maps instruction indexes to whether or not they were executed.
   394  	// This slice has the same size as the number of instructions as the BPF
   395  	// program that was run, so it can be used as a way to get the program size.
   396  	// Since an instruction can never run twice in BPF, this can also be used
   397  	// to determine how many instructions were executed.
   398  	Coverage []bool
   399  
   400  	// InputAccessed maps input byte offsets to whether or not they were
   401  	// read by the program during execution.
   402  	InputAccessed []bool
   403  }
   404  
   405  // String returns a human-readable view of an `Execution`.
   406  func (e *ExecutionMetrics) String() string {
   407  	type intRange struct {
   408  		from, to int
   409  	}
   410  
   411  	// addRangeString formats an `intRange` and writes it to `sb`.
   412  	addRangeString := func(sb *strings.Builder, rng intRange) {
   413  		if rng.from == rng.to {
   414  			sb.WriteString(strconv.Itoa(rng.from))
   415  		} else {
   416  			sb.WriteString(strconv.Itoa(rng.from))
   417  			sb.WriteRune('-')
   418  			sb.WriteString(strconv.Itoa(rng.to))
   419  		}
   420  	}
   421  
   422  	// `getRanges` takes a slice of booleans and returns ranges of all-true
   423  	// indexes.
   424  	getRanges := func(s []bool) []intRange {
   425  		var ranges []intRange
   426  		firstTrueIndex := -1
   427  		for i, covered := range s {
   428  			if covered {
   429  				if firstTrueIndex == -1 {
   430  					firstTrueIndex = i
   431  				}
   432  				continue
   433  			}
   434  			if firstTrueIndex != -1 {
   435  				ranges = append(ranges, intRange{firstTrueIndex, i - 1})
   436  				firstTrueIndex = -1
   437  			}
   438  		}
   439  		if firstTrueIndex != -1 {
   440  			ranges = append(ranges, intRange{firstTrueIndex, len(s) - 1})
   441  		}
   442  		return ranges
   443  	}
   444  
   445  	// ranges returns a human-friendly representation of the
   446  	// ranges of items in `s` that are contiguously `true`.
   447  	ranges := func(s []bool) string {
   448  		if len(s) == 0 {
   449  			return "empty"
   450  		}
   451  		allFalse := true
   452  		allTrue := true
   453  		for _, v := range s {
   454  			if v {
   455  				allFalse = false
   456  			} else {
   457  				allTrue = false
   458  			}
   459  		}
   460  		if allFalse {
   461  			return "none"
   462  		}
   463  		if allTrue {
   464  			return "all"
   465  		}
   466  		ranges := getRanges(s)
   467  		var sb strings.Builder
   468  		for i, rng := range ranges {
   469  			if i != 0 {
   470  				sb.WriteRune(',')
   471  			}
   472  			addRangeString(&sb, rng)
   473  		}
   474  		return sb.String()
   475  	}
   476  	executedInstructions := 0
   477  	for _, covered := range e.Coverage {
   478  		if covered {
   479  			executedInstructions++
   480  		}
   481  	}
   482  	return fmt.Sprintf("returned %d, covered %d/%d instructions (%s), read input bytes %s (%d total input bytes)", e.ReturnValue, executedInstructions, len(e.Coverage), ranges(e.Coverage), ranges(e.InputAccessed), len(e.InputAccessed))
   483  }
   484  
   485  // markInputRead marks the `bytesRead` bytes starting at `offset` as having
   486  // been read from the input. This function assumes that the offset and number
   487  // of bytes have already been verified as valid.
   488  func (e *ExecutionMetrics) markInputRead(offset uint32, bytesRead int) {
   489  	if int(offset)+bytesRead > len(e.InputAccessed) {
   490  		panic(fmt.Sprintf("invalid offset or number of bytes read: offset=%d bytesRead=%d len=%d", offset, bytesRead, len(e.InputAccessed)))
   491  	}
   492  	for i := 0; i < bytesRead; i++ {
   493  		e.InputAccessed[int(offset)+i] = true
   494  	}
   495  }
   496  
   497  // InstrumentedExec executes a BPF program over the given input while
   498  // instrumenting it: recording memory accesses and lines executed.
   499  // This is slower than Exec, but should return equivalent results.
   500  func InstrumentedExec[endian Endianness](p Program, in Input) (ExecutionMetrics, error) {
   501  	ret := ExecutionMetrics{
   502  		Coverage:      make([]bool, len(p.instructions)),
   503  		InputAccessed: make([]bool, len(in)),
   504  	}
   505  	var m machine
   506  	var pc int
   507  	for ; pc < len(p.instructions); pc++ {
   508  		ret.Coverage[pc] = true
   509  		i := p.instructions[pc]
   510  		switch i.OpCode {
   511  		case Ld | Imm | W:
   512  			m.A = i.K
   513  		case Ld | Abs | W:
   514  			val, ok := load32[endian](in, i.K)
   515  			if !ok {
   516  				return ret, Error{InvalidLoad, pc}
   517  			}
   518  			ret.markInputRead(i.K, 4)
   519  			m.A = val
   520  		case Ld | Abs | H:
   521  			val, ok := load16[endian](in, i.K)
   522  			if !ok {
   523  				return ret, Error{InvalidLoad, pc}
   524  			}
   525  			ret.markInputRead(i.K, 2)
   526  			m.A = uint32(val)
   527  		case Ld | Abs | B:
   528  			val, ok := load8(in, i.K)
   529  			if !ok {
   530  				return ret, Error{InvalidLoad, pc}
   531  			}
   532  			ret.markInputRead(i.K, 1)
   533  			m.A = uint32(val)
   534  		case Ld | Ind | W:
   535  			val, ok := load32[endian](in, m.X+i.K)
   536  			if !ok {
   537  				return ret, Error{InvalidLoad, pc}
   538  			}
   539  			ret.markInputRead(m.X+i.K, 4)
   540  			m.A = val
   541  		case Ld | Ind | H:
   542  			val, ok := load16[endian](in, m.X+i.K)
   543  			if !ok {
   544  				return ret, Error{InvalidLoad, pc}
   545  			}
   546  			ret.markInputRead(m.X+i.K, 2)
   547  			m.A = uint32(val)
   548  		case Ld | Ind | B:
   549  			val, ok := load8(in, m.X+i.K)
   550  			if !ok {
   551  				return ret, Error{InvalidLoad, pc}
   552  			}
   553  			ret.markInputRead(m.X+i.K, 1)
   554  			m.A = uint32(val)
   555  		case Ld | Mem | W:
   556  			m.A = m.M[int(i.K)]
   557  		case Ld | Len | W:
   558  			m.A = uint32(len(in))
   559  		case Ldx | Imm | W:
   560  			m.X = i.K
   561  		case Ldx | Mem | W:
   562  			m.X = m.M[int(i.K)]
   563  		case Ldx | Len | W:
   564  			m.X = uint32(len(in))
   565  		case Ldx | Msh | B:
   566  			val, ok := load8(in, i.K)
   567  			if !ok {
   568  				return ret, Error{InvalidLoad, pc}
   569  			}
   570  			ret.markInputRead(i.K, 1)
   571  			m.X = 4 * uint32(val&0xf)
   572  		case St:
   573  			m.M[int(i.K)] = m.A
   574  		case Stx:
   575  			m.M[int(i.K)] = m.X
   576  		case Alu | Add | K:
   577  			m.A += i.K
   578  		case Alu | Add | X:
   579  			m.A += m.X
   580  		case Alu | Sub | K:
   581  			m.A -= i.K
   582  		case Alu | Sub | X:
   583  			m.A -= m.X
   584  		case Alu | Mul | K:
   585  			m.A *= i.K
   586  		case Alu | Mul | X:
   587  			m.A *= m.X
   588  		case Alu | Div | K:
   589  			// K != 0 already checked by Compile.
   590  			m.A /= i.K
   591  		case Alu | Div | X:
   592  			if m.X == 0 {
   593  				return ret, Error{DivisionByZero, pc}
   594  			}
   595  			m.A /= m.X
   596  		case Alu | Or | K:
   597  			m.A |= i.K
   598  		case Alu | Or | X:
   599  			m.A |= m.X
   600  		case Alu | And | K:
   601  			m.A &= i.K
   602  		case Alu | And | X:
   603  			m.A &= m.X
   604  		case Alu | Lsh | K:
   605  			m.A <<= i.K
   606  		case Alu | Lsh | X:
   607  			m.A <<= m.X
   608  		case Alu | Rsh | K:
   609  			m.A >>= i.K
   610  		case Alu | Rsh | X:
   611  			m.A >>= m.X
   612  		case Alu | Neg:
   613  			m.A = uint32(-int32(m.A))
   614  		case Alu | Mod | K:
   615  			// K != 0 already checked by Compile.
   616  			m.A %= i.K
   617  		case Alu | Mod | X:
   618  			if m.X == 0 {
   619  				return ret, Error{DivisionByZero, pc}
   620  			}
   621  			m.A %= m.X
   622  		case Alu | Xor | K:
   623  			m.A ^= i.K
   624  		case Alu | Xor | X:
   625  			m.A ^= m.X
   626  		case Jmp | Ja:
   627  			pc += int(i.K)
   628  		case Jmp | Jeq | K:
   629  			pc += conditionalJumpOffset(i, m.A == i.K)
   630  		case Jmp | Jeq | X:
   631  			pc += conditionalJumpOffset(i, m.A == m.X)
   632  		case Jmp | Jgt | K:
   633  			pc += conditionalJumpOffset(i, m.A > i.K)
   634  		case Jmp | Jgt | X:
   635  			pc += conditionalJumpOffset(i, m.A > m.X)
   636  		case Jmp | Jge | K:
   637  			pc += conditionalJumpOffset(i, m.A >= i.K)
   638  		case Jmp | Jge | X:
   639  			pc += conditionalJumpOffset(i, m.A >= m.X)
   640  		case Jmp | Jset | K:
   641  			pc += conditionalJumpOffset(i, (m.A&i.K) != 0)
   642  		case Jmp | Jset | X:
   643  			pc += conditionalJumpOffset(i, (m.A&m.X) != 0)
   644  		case Ret | K:
   645  			ret.ReturnValue = i.K
   646  			return ret, nil
   647  		case Ret | A:
   648  			ret.ReturnValue = m.A
   649  			return ret, nil
   650  		case Misc | Tax:
   651  			m.A = m.X
   652  		case Misc | Txa:
   653  			m.X = m.A
   654  		default:
   655  			return ret, Error{InvalidOpcode, pc}
   656  		}
   657  	}
   658  	return ret, Error{InvalidEndOfProgram, pc}
   659  }