{"record":{"id":"496293c6bc9d4799","repo":"cilium/cilium","slug":"evaluating-branch-w","errorCode":null,"errorMessage":"evaluating branch: %w","messagePattern":"evaluating branch: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/bpf/analyze/reachability.go","lineNumber":456,"sourceCode":"\t// field register contains the dereferenced value of the config variable.\n\t//\n\t// Example:\n\t//\t0: LoadMapValue dst: r1, fd: 0 off: 4 <.rodata.config>\n\t//\t2: LdXMemB dst: r2 src: r1 off: 0 imm: 0\n\t//\t3: JNEImm dst: r2 off: 2 imm: 0\n\tcase asm.ImmSource:\n\t\tdst, err := resolveRegister(bt, branch.Dst, vars)\n\t\tif errors.Is(err, errUnpredictable) {\n\t\t\t// Don't wrap err since this is a hot path.\n\t\t\treturn false, err\n\t\t}\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"resolving dst register %s: %w\", branch.Dst, err)\n\t\t}\n\n\t\tjump, err := evalJumpOp(branch.OpCode, dst, branch.Constant)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"evaluating branch: %w\", err)\n\t\t}\n\n\t\treturn jump, nil\n\n\t// Register comparisons require finding both a map load and an immediate\n\t// load into the two registers used by the branch instruction.\n\t//\n\t// Example:\n\t//\t0: LoadMapValue dst: r1, fd: 0 off: 4 <.rodata.config>\n\t//\t2: LdXMemDW dst: r1 src: r1 off: 0 imm: 0\n\t//\t3: LdImmDW dst: r2 imm: 42\n\t//\t5: JGTReg dst: r1 src: r2 off: 2\n\t//\n\t// Note that src and reg may be swapped depending on the comparison op and the\n\t// compiler's mood. During initial testing, the config value was more often\n\t// found in dst.\n\tcase asm.RegSource:\n\t\tdst, err := resolveRegister(bt.Clone(), branch.Dst, vars)","sourceCodeStart":438,"sourceCodeEnd":474,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/bpf/analyze/reachability.go#L438-L474","documentation":"predictBranch wraps errors from evalJumpOp when evaluating an immediate comparison between the resolved dst value and the branch's constant. This means the register resolved fine, but the jump opcode itself could not be evaluated.","triggerScenarios":"evalJumpOp is given a jump OpCode it does not recognize or support (e.g. unsupported jump operators like JSET passed through this path, or a non-jump opcode reaching the evaluator).","commonSituations":"Compiler emits jump variants (e.g. JSET imm, 32-bit jump ops JA32) that evalJumpOp doesn't handle for the ImmSource path.","solutions":["Check branch.OpCode in the failing instruction and confirm evalJumpOp supports that jump operator.","Add a case for the missing operator in evalJumpOp if you own the code.","As a workaround, ensure such branches are classified as unpredictable (handled via unpredictableBlock) rather than reaching the evaluator."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"supported := map[asm.JumpOp]bool{\n\tasm.Ja: true, asm.JEq: true, asm.JNE: true, asm.JGT: true,\n\tasm.JGE: true, asm.JLT: true, asm.JLE: true, asm.JSGT: true,\n\tasm.JSGE: true, asm.JSLT: true, asm.JSLE: true,\n}\nif branch.OpCode.JumpOp() == 0 && !supported[branch.OpCode.JumpOp()] {\n\t// treat as unpredictable instead of evaluating\n}","typeGuard":"func isSupportedJumpOp(op asm.OpCode) bool {\n\tswitch op.JumpOp() {\n\tcase asm.Ja, asm.JEq, asm.JNE, asm.JGT, asm.JGE, asm.JLT, asm.JLE,\n\t\tasm.JSet, asm.JSGT, asm.JSGE, asm.JSLT, asm.JSLE:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"jump, err := evalJumpOp(branch.OpCode, dst, branch.Constant)\nif err != nil {\n\treturn false, errUnpredictable // degrade to conservative visit\n}","preventionTips":["Enumerate all jump operators your toolchain can emit and cover them in evalJumpOp.","Add unit tests per JumpOp including 32-bit variants."],"tags":["bpf","jump-evaluation","error-wrapping"],"backgroundTag":"bpf-unsupported-jump-opcode","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}