github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/bpf/decoder.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  	"bytes"
    19  	"fmt"
    20  
    21  	"github.com/sagernet/gvisor/pkg/abi/linux"
    22  )
    23  
    24  // DecodeProgram translates a compiled BPF program into text format.
    25  func DecodeProgram(p Program) (string, error) {
    26  	return DecodeInstructions(p.instructions)
    27  }
    28  
    29  // DecodeInstructions translates an array of BPF instructions into text format.
    30  func DecodeInstructions(instns []Instruction) (string, error) {
    31  	var ret bytes.Buffer
    32  	for line, s := range instns {
    33  		ret.WriteString(fmt.Sprintf("%v: ", line))
    34  		if err := decode(s, line, &ret); err != nil {
    35  			return "", err
    36  		}
    37  		ret.WriteString("\n")
    38  	}
    39  	return ret.String(), nil
    40  }
    41  
    42  // Decode translates a single BPF instruction into text format.
    43  func Decode(ins Instruction) (string, error) {
    44  	var ret bytes.Buffer
    45  	err := decode(ins, -1, &ret)
    46  	return ret.String(), err
    47  }
    48  
    49  func decode(inst Instruction, line int, w *bytes.Buffer) error {
    50  	var err error
    51  	switch inst.OpCode & instructionClassMask {
    52  	case Ld:
    53  		err = decodeLd(inst, w)
    54  	case Ldx:
    55  		err = decodeLdx(inst, w)
    56  	case St:
    57  		w.WriteString(fmt.Sprintf("M[%v] <- A", inst.K))
    58  	case Stx:
    59  		w.WriteString(fmt.Sprintf("M[%v] <- X", inst.K))
    60  	case Alu:
    61  		err = decodeAlu(inst, w)
    62  	case Jmp:
    63  		err = decodeJmp(inst, line, w)
    64  	case Ret:
    65  		err = decodeRet(inst, w)
    66  	case Misc:
    67  		err = decodeMisc(inst, w)
    68  	default:
    69  		return fmt.Errorf("invalid BPF instruction: %v", linux.BPFInstruction(inst))
    70  	}
    71  	return err
    72  }
    73  
    74  // A <- P[k:4]
    75  func decodeLd(inst Instruction, w *bytes.Buffer) error {
    76  	w.WriteString("A <- ")
    77  
    78  	switch inst.OpCode & loadModeMask {
    79  	case Imm:
    80  		w.WriteString(fmt.Sprintf("%v", inst.K))
    81  	case Abs:
    82  		w.WriteString(fmt.Sprintf("P[%v:", inst.K))
    83  		if err := decodeLdSize(inst, w); err != nil {
    84  			return err
    85  		}
    86  		w.WriteString("]")
    87  	case Ind:
    88  		w.WriteString(fmt.Sprintf("P[X+%v:", inst.K))
    89  		if err := decodeLdSize(inst, w); err != nil {
    90  			return err
    91  		}
    92  		w.WriteString("]")
    93  	case Mem:
    94  		w.WriteString(fmt.Sprintf("M[%v]", inst.K))
    95  	case Len:
    96  		w.WriteString("len")
    97  	default:
    98  		return fmt.Errorf("invalid BPF LD instruction: %v", linux.BPFInstruction(inst))
    99  	}
   100  	return nil
   101  }
   102  
   103  func decodeLdSize(inst Instruction, w *bytes.Buffer) error {
   104  	switch inst.OpCode & loadSizeMask {
   105  	case W:
   106  		w.WriteString("4")
   107  	case H:
   108  		w.WriteString("2")
   109  	case B:
   110  		w.WriteString("1")
   111  	default:
   112  		return fmt.Errorf("invalid BPF LD size: %v", linux.BPFInstruction(inst))
   113  	}
   114  	return nil
   115  }
   116  
   117  // X <- P[k:4]
   118  func decodeLdx(inst Instruction, w *bytes.Buffer) error {
   119  	w.WriteString("X <- ")
   120  
   121  	switch inst.OpCode & loadModeMask {
   122  	case Imm:
   123  		w.WriteString(fmt.Sprintf("%v", inst.K))
   124  	case Mem:
   125  		w.WriteString(fmt.Sprintf("M[%v]", inst.K))
   126  	case Len:
   127  		w.WriteString("len")
   128  	case Msh:
   129  		w.WriteString(fmt.Sprintf("4*(P[%v:1]&0xf)", inst.K))
   130  	default:
   131  		return fmt.Errorf("invalid BPF LDX instruction: %v", linux.BPFInstruction(inst))
   132  	}
   133  	return nil
   134  }
   135  
   136  // A <- A + k
   137  func decodeAlu(inst Instruction, w *bytes.Buffer) error {
   138  	code := inst.OpCode & aluMask
   139  	if code == Neg {
   140  		w.WriteString("A <- -A")
   141  		return nil
   142  	}
   143  
   144  	w.WriteString("A <- A ")
   145  	switch code {
   146  	case Add:
   147  		w.WriteString("+ ")
   148  	case Sub:
   149  		w.WriteString("- ")
   150  	case Mul:
   151  		w.WriteString("* ")
   152  	case Div:
   153  		w.WriteString("/ ")
   154  	case Or:
   155  		w.WriteString("| ")
   156  	case And:
   157  		w.WriteString("& ")
   158  	case Lsh:
   159  		w.WriteString("<< ")
   160  	case Rsh:
   161  		w.WriteString(">> ")
   162  	case Mod:
   163  		w.WriteString("% ")
   164  	case Xor:
   165  		w.WriteString("^ ")
   166  	default:
   167  		return fmt.Errorf("invalid BPF ALU instruction: %v", linux.BPFInstruction(inst))
   168  	}
   169  	return decodeSource(inst, w)
   170  }
   171  
   172  func decodeSource(inst Instruction, w *bytes.Buffer) error {
   173  	switch inst.OpCode & srcAluJmpMask {
   174  	case K:
   175  		w.WriteString(fmt.Sprintf("%v", inst.K))
   176  	case X:
   177  		w.WriteString("X")
   178  	default:
   179  		return fmt.Errorf("invalid BPF ALU/JMP source instruction: %v", linux.BPFInstruction(inst))
   180  	}
   181  	return nil
   182  }
   183  
   184  // pc += (A > k) ? jt : jf
   185  func decodeJmp(inst Instruction, line int, w *bytes.Buffer) error {
   186  	code := inst.OpCode & jmpMask
   187  
   188  	w.WriteString("pc += ")
   189  	if code == Ja {
   190  		w.WriteString(printJmpTarget(inst.K, line))
   191  	} else {
   192  		w.WriteString("(A ")
   193  		switch code {
   194  		case Jeq:
   195  			w.WriteString("== ")
   196  		case Jgt:
   197  			w.WriteString("> ")
   198  		case Jge:
   199  			w.WriteString(">= ")
   200  		case Jset:
   201  			w.WriteString("& ")
   202  		default:
   203  			return fmt.Errorf("invalid BPF ALU instruction: %v", linux.BPFInstruction(inst))
   204  		}
   205  		if err := decodeSource(inst, w); err != nil {
   206  			return err
   207  		}
   208  		w.WriteString(
   209  			fmt.Sprintf(") ? %s : %s",
   210  				printJmpTarget(uint32(inst.JumpIfTrue), line),
   211  				printJmpTarget(uint32(inst.JumpIfFalse), line)))
   212  	}
   213  	return nil
   214  }
   215  
   216  func printJmpTarget(target uint32, line int) string {
   217  	if line == -1 {
   218  		return fmt.Sprintf("%v", target)
   219  	}
   220  	return fmt.Sprintf("%v [%v]", target, int(target)+line+1)
   221  }
   222  
   223  // ret k
   224  func decodeRet(inst Instruction, w *bytes.Buffer) error {
   225  	w.WriteString("ret ")
   226  
   227  	code := inst.OpCode & srcRetMask
   228  	switch code {
   229  	case K:
   230  		w.WriteString(fmt.Sprintf("%v", inst.K))
   231  	case A:
   232  		w.WriteString("A")
   233  	default:
   234  		return fmt.Errorf("invalid BPF RET source instruction: %v", linux.BPFInstruction(inst))
   235  	}
   236  	return nil
   237  }
   238  
   239  func decodeMisc(inst Instruction, w *bytes.Buffer) error {
   240  	code := inst.OpCode & miscMask
   241  	switch code {
   242  	case Tax:
   243  		w.WriteString("X <- A")
   244  	case Txa:
   245  		w.WriteString("A <- X")
   246  	default:
   247  		return fmt.Errorf("invalid BPF ALU/JMP source instruction: %v", linux.BPFInstruction(inst))
   248  	}
   249  	return nil
   250  }