github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/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 }