func TestNoAbsoluteGuard(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		bpf.LoadConstant{Dst: bpf.RegA, Val: 23},
		bpf.RetA{},
	})
	blocks := mustSplitBlocks(t, 1, insns)
	addAbsolutePacketGuards(blocks)
	matchBlock(t, blocks[0], insns, nil)
}
Check we don't add a guard if there are no packet loads
package cbpfc
import (
	"reflect"
	"strings"
	"testing"
	"golang.org/x/net/bpf"
)
func requireError(tb testing.TB, err error, contains string) {
	tb.Helper()
	if err == nil {
		tb.Fatalf("expected error %s", contains)
	}
	if !strings.Contains(err.Error(), contains) {
		tb.Fatalf("error %v does not contain %s", err, contains)
	}
}
func TestZero(t *testing.T) {
	_, err := compile([]bpf.Instruction{})
	requireError(t, err, "can't compile 0 instructions")
}
func TestRaw(t *testing.T) {
	_, err := compile([]bpf.Instruction{
		bpf.RawInstruction{},
	})
	requireError(t, err, "unsupported instruction 0:")
}
func TestExtension(t *testing.T) {
	for i := 0; i < 256; i++ {
		ext := bpf.Extension(i)
		_, err := compile([]bpf.Instruction{
			bpf.LoadExtension{Num: ext},
			bpf.RetA{},
		})
		switch ext {
		case bpf.ExtLen:
			if err != nil {
				t.Fatal("ExtLen not accepted", err)
			}
		default:
			requireError(t, err, "unsupported BPF extension 0:")
		}
	}
}
func TestJumpOut(t *testing.T) {
	_, err := compile([]bpf.Instruction{
		bpf.LoadConstant{Dst: bpf.RegX, Val: 0},
		bpf.Jump{Skip: 0},
	})
	requireError(t, err, "instruction 1: ja 0 flows past last instruction")
}
func TestJumpIfOut(t *testing.T) {
	_, err := compile([]bpf.Instruction{
		bpf.LoadConstant{Dst: bpf.RegA, Val: 0},
		bpf.JumpIf{Cond: bpf.JumpEqual, Val: 2, SkipTrue: 0, SkipFalse: 1},
	})
	requireError(t, err, "instruction 1: jneq #2,1 flows past last instruction")
}
func TestJumpIfXOut(t *testing.T) {
	_, err := compile([]bpf.Instruction{
		bpf.LoadConstant{Dst: bpf.RegA, Val: 0},
		bpf.LoadConstant{Dst: bpf.RegX, Val: 3},
		bpf.JumpIfX{Cond: bpf.JumpEqual, SkipTrue: 1, SkipFalse: 0},
	})
	requireError(t, err, "instruction 2: jeq x,1 flows past last instruction")
}
func TestFallthroughOut(t *testing.T) {
	_, err := compile([]bpf.Instruction{
		bpf.LoadConstant{Dst: bpf.RegA, Val: 0},
	})
	requireError(t, err, "instruction 0: ld #0 flows past last instruction")
}
func TestNormalizeJumps(t *testing.T) {
	insns := func(skipTrue, skipFalse uint8) []instruction {
		return toInstructions([]bpf.Instruction{
			bpf.JumpIf{Cond: bpf.JumpEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpNotEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpNotEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpGreaterThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpGreaterThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpLessThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpLessThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpGreaterOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpGreaterOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpLessOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpLessOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpBitsSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpBitsSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpBitsNotSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpBitsNotSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
		})
	}
	invertedInsns := func(skipTrue, skipFalse uint8) []instruction {
		return toInstructions([]bpf.Instruction{
			bpf.JumpIf{Cond: bpf.JumpNotEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpNotEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpLessOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpLessOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpGreaterOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpGreaterOrEqual, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpLessThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpLessThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpGreaterThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpGreaterThan, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpBitsNotSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpBitsNotSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIf{Cond: bpf.JumpBitsSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
			bpf.JumpIfX{Cond: bpf.JumpBitsSet, SkipTrue: skipTrue, SkipFalse: skipFalse},
		})
	}
	check := func(t *testing.T, input []instruction, expected []instruction) {
		normalizeJumps(input)
		if !reflect.DeepEqual(input, expected) {
			t.Fatalf("\nGot:\n%v\n\nExpected:\n%v", input, expected)
		}
	}
	check(t, insns(1, 0), insns(1, 0))
	check(t, insns(1, 3), insns(1, 3))
	check(t, insns(0, 3), invertedInsns(3, 0))
}
func TestInstructionReadsRegA(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: true,
		bpf.ALUOpX{}:        true,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  true,
		bpf.JumpIfX{}: true,
		bpf.LoadAbsolute{}:              false,
		bpf.LoadConstant{Dst: bpf.RegA}: false,
		bpf.LoadConstant{Dst: bpf.RegX}: false,
		bpf.LoadExtension{}:             false,
		bpf.LoadIndirect{}:              false,
		bpf.LoadMemShift{}:              false,
		bpf.LoadScratch{Dst: bpf.RegA}:  false,
		bpf.LoadScratch{Dst: bpf.RegX}:  false,
		bpf.NegateA{}: true,
		bpf.RetA{}:        true,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA}: true,
		bpf.StoreScratch{Src: bpf.RegX}: false,
		bpf.TAX{}: true,
		bpf.TXA{}: false,
	}, func(insn bpf.Instruction) bool {
		return memReads(insn).regs[bpf.RegA]
	})
}
func TestInstructionWritesRegA(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: true,
		bpf.ALUOpX{}:        true,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  false,
		bpf.JumpIfX{}: false,
		bpf.LoadAbsolute{}:              true,
		bpf.LoadConstant{Dst: bpf.RegA}: true,
		bpf.LoadConstant{Dst: bpf.RegX}: false,
		bpf.LoadExtension{}:             true,
		bpf.LoadIndirect{}:              true,
		bpf.LoadMemShift{}:              false,
		bpf.LoadScratch{Dst: bpf.RegA}:  true,
		bpf.LoadScratch{Dst: bpf.RegX}:  false,
		bpf.NegateA{}: true,
		bpf.RetA{}:        false,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA}: false,
		bpf.StoreScratch{Src: bpf.RegX}: false,
		bpf.TAX{}: false,
		bpf.TXA{}: true,
	}, func(insn bpf.Instruction) bool {
		return memWrites(insn).regs[bpf.RegA]
	})
}
func TestInstructionReadsRegX(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: false,
		bpf.ALUOpX{}:        true,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  false,
		bpf.JumpIfX{}: true,
		bpf.LoadAbsolute{}:              false,
		bpf.LoadConstant{Dst: bpf.RegA}: false,
		bpf.LoadConstant{Dst: bpf.RegX}: false,
		bpf.LoadExtension{}:             false,
		bpf.LoadIndirect{}:              true,
		bpf.LoadMemShift{}:              false,
		bpf.LoadScratch{Dst: bpf.RegA}:  false,
		bpf.LoadScratch{Dst: bpf.RegX}:  false,
		bpf.NegateA{}: false,
		bpf.RetA{}:        false,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA}: false,
		bpf.StoreScratch{Src: bpf.RegX}: true,
		bpf.TAX{}: false,
		bpf.TXA{}: true,
	}, func(insn bpf.Instruction) bool {
		return memReads(insn).regs[bpf.RegX]
	})
}
func TestInstructionWritesRegX(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: false,
		bpf.ALUOpX{}:        false,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  false,
		bpf.JumpIfX{}: false,
		bpf.LoadAbsolute{}:              false,
		bpf.LoadConstant{Dst: bpf.RegA}: false,
		bpf.LoadConstant{Dst: bpf.RegX}: true,
		bpf.LoadExtension{}:             false,
		bpf.LoadIndirect{}:              false,
		bpf.LoadMemShift{}:              true,
		bpf.LoadScratch{Dst: bpf.RegA}:  false,
		bpf.LoadScratch{Dst: bpf.RegX}:  true,
		bpf.NegateA{}: false,
		bpf.RetA{}:        false,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA}: false,
		bpf.StoreScratch{Src: bpf.RegX}: false,
		bpf.TAX{}: true,
		bpf.TXA{}: false,
	}, func(insn bpf.Instruction) bool {
		return memWrites(insn).regs[bpf.RegX]
	})
}
func TestInstructionReadsScratch(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: false,
		bpf.ALUOpX{}:        false,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  false,
		bpf.JumpIfX{}: false,
		bpf.LoadAbsolute{}:                   false,
		bpf.LoadConstant{Dst: bpf.RegA}:      false,
		bpf.LoadConstant{Dst: bpf.RegX}:      false,
		bpf.LoadExtension{}:                  false,
		bpf.LoadIndirect{}:                   false,
		bpf.LoadMemShift{}:                   false,
		bpf.LoadScratch{Dst: bpf.RegA, N: 3}: true,
		bpf.LoadScratch{Dst: bpf.RegX, N: 3}: true,
		bpf.NegateA{}: false,
		bpf.RetA{}:        false,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA, N: 3}: false,
		bpf.StoreScratch{Src: bpf.RegX, N: 3}: false,
		bpf.TAX{}: false,
		bpf.TXA{}: false,
	}, func(insn bpf.Instruction) bool {
		return memReads(insn).scratch[3]
	})
}
func TestInstructionWritesScratch(t *testing.T) {
	checkMemoryStatus(t, map[bpf.Instruction]bool{
		bpf.ALUOpConstant{}: false,
		bpf.ALUOpX{}:        false,
		bpf.Jump{}:    false,
		bpf.JumpIf{}:  false,
		bpf.JumpIfX{}: false,
		bpf.LoadAbsolute{}:                   false,
		bpf.LoadConstant{Dst: bpf.RegA}:      false,
		bpf.LoadConstant{Dst: bpf.RegX}:      false,
		bpf.LoadExtension{}:                  false,
		bpf.LoadIndirect{}:                   false,
		bpf.LoadMemShift{}:                   false,
		bpf.LoadScratch{Dst: bpf.RegA, N: 3}: false,
		bpf.LoadScratch{Dst: bpf.RegX, N: 3}: false,
		bpf.NegateA{}: false,
		bpf.RetA{}:        false,
		bpf.RetConstant{}: false,
		bpf.StoreScratch{Src: bpf.RegA, N: 3}: true,
		bpf.StoreScratch{Src: bpf.RegX, N: 3}: true,
		bpf.TAX{}: false,
		bpf.TXA{}: false,
	}, func(insn bpf.Instruction) bool {
		return memWrites(insn).scratch[3]
	})
}
func checkMemoryStatus(t *testing.T, expected map[bpf.Instruction]bool, test func(bpf.Instruction) bool) {
	t.Helper()
	for insn, value := range expected {
		if test(insn) != value {
			t.Fatalf("Instruction %v expected %v got %v", insn, value, test(insn))
		}
	}
}
func TestUninitializedReg(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.RetA{},
	})
	blocks := mustSplitBlocks(t, 1, insns)
	err := initializeMemory(blocks)
	if err != nil {
		t.Fatal(err)
	}
	matchBlock(t, blocks[0], join(
		[]instruction{{Instruction: bpf.LoadConstant{Dst: bpf.RegA, Val: 0}}},
		insns,
	), nil)
}
func TestPartiallyUninitializedReg(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadConstant{Dst: bpf.RegA, Val: 3},
		  bpf.JumpIf{Cond: bpf.JumpEqual, Val: 3, SkipTrue: 0, SkipFalse: 1},  
		  bpf.TAX{},  
		  bpf.TXA{},  
		  bpf.RetA{},
	})
	blocks := mustSplitBlocks(t, 3, insns)
	err := initializeMemory(blocks)
	if err != nil {
		t.Fatal(err)
	}
	matchBlock(t, blocks[0], join(
		[]instruction{{Instruction: bpf.LoadConstant{Dst: bpf.RegX, Val: 0}}},
		insns[:2],
	), nil)
	matchBlock(t, blocks[1], insns[2:3], nil)
	matchBlock(t, blocks[2], insns[3:], nil)
}
func TestUninitializedScratch(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadScratch{Dst: bpf.RegA, N: 2},
		  bpf.RetA{},
	})
	blocks := mustSplitBlocks(t, 1, insns)
	requireError(t, initializeMemory(blocks), "instruction 0: ld M[2] reads potentially uninitialized scratch register M[2]")
}
func TestPartiallyUninitializedScratch(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadConstant{Dst: bpf.RegA, Val: 3},
		  bpf.JumpIf{Cond: bpf.JumpEqual, Val: 3, SkipTrue: 0, SkipFalse: 1},  
		  bpf.StoreScratch{Src: bpf.RegA, N: 5},  
		  bpf.LoadScratch{Dst: bpf.RegA, N: 5},
		  bpf.RetA{},
	})
	blocks := mustSplitBlocks(t, 3, insns)
	requireError(t, initializeMemory(blocks), "instruction 3: ld M[5] reads potentially uninitialized scratch register M[5]")
}
func TestBlocksJump(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadConstant{Dst: bpf.RegX, Val: 3},
		  bpf.Jump{Skip: 1},
		  bpf.RetConstant{Val: 0},  
		  bpf.RetConstant{Val: 1},
	})
	blocks := mustSplitBlocks(t, 2, insns)
	matchBlock(t, blocks[0], insns[:2], map[pos]*block{3: blocks[1]})
	matchBlock(t, blocks[1], insns[3:], map[pos]*block{})
}
func TestBlocksJumpIf(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadConstant{Dst: bpf.RegA, Val: 0},
		  bpf.JumpIf{Cond: bpf.JumpEqual, Val: 3, SkipTrue: 1, SkipFalse: 0},
		  bpf.RetConstant{Val: 0},
		  bpf.RetConstant{Val: 1},
	})
	blocks := mustSplitBlocks(t, 3, insns)
	matchBlock(t, blocks[0], insns[0:2], map[pos]*block{2: blocks[1], 3: blocks[2]})
	matchBlock(t, blocks[1], insns[2:3], map[pos]*block{})
	matchBlock(t, blocks[2], insns[3:4], map[pos]*block{})
}
func TestBlocksJumpIfX(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		  bpf.LoadConstant{Dst: bpf.RegA, Val: 0},
		  bpf.LoadConstant{Dst: bpf.RegX, Val: 3},
		  bpf.JumpIfX{Cond: bpf.JumpEqual, SkipTrue: 1, SkipFalse: 0},
		  bpf.RetConstant{Val: 0},
		  bpf.RetConstant{Val: 1},
	})
	blocks := mustSplitBlocks(t, 3, insns)
	matchBlock(t, blocks[0], insns[0:3], map[pos]*block{3: blocks[1], 4: blocks[2]})
	matchBlock(t, blocks[1], insns[3:4], map[pos]*block{})
	matchBlock(t, blocks[2], insns[4:5], map[pos]*block{})
}
func TestDivisionByZeroImm(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		blocks := mustSplitBlocks(t, 1, toInstructions([]bpf.Instruction{
			bpf.ALUOpConstant{Op: op, Val: 0},
			bpf.RetConstant{},
		}))
		requireError(t, addDivideByZeroGuards(blocks), "divides by 0")
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroX(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			bpf.LoadAbsolute{Size: 1, Off: 0},
			bpf.TXA{},
			bpf.ALUOpX{Op: op},
			bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 1, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroXTwice(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			bpf.LoadAbsolute{Size: 1, Off: 0},
			bpf.TXA{},
			bpf.ALUOpX{Op: op},
			bpf.ALUOpX{Op: op},
			bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 1, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroXConstant(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			bpf.LoadAbsolute{Size: 1, Off: 0},
			bpf.TXA{},
			bpf.ALUOpX{Op: op},
			bpf.LoadConstant{Dst: bpf.RegX},  
			bpf.ALUOpX{Op: op},
			bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 1, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:4],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[4:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroXMemShift(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			bpf.LoadAbsolute{Size: 1, Off: 0},
			bpf.TXA{},
			bpf.ALUOpX{Op: op},
			bpf.LoadMemShift{Off: 2},  
			bpf.ALUOpX{Op: op},
			bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 1, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:4],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[4:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroXTXA(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			bpf.LoadAbsolute{Size: 1, Off: 0},
			bpf.TXA{},
			bpf.ALUOpX{Op: op},
			bpf.TAX{},  
			bpf.ALUOpX{Op: op},
			bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 1, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:4],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[4:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroParentsOK(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			  bpf.LoadAbsolute{Size: 1, Off: 0},
			  bpf.TXA{},
			  bpf.ALUOpX{Op: op},
			  bpf.JumpIf{Cond: bpf.JumpEqual, Val: 3, SkipTrue: 0, SkipFalse: 2},  
			  bpf.LoadAbsolute{Size: 1, Off: 1},
			  bpf.Jump{Skip: 1},  
			  bpf.LoadAbsolute{Size: 1, Off: 2},
			  bpf.ALUOpX{Op: op},
			  bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 4, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:4],
		), nil)
		matchBlock(t, blocks[1], insns[4:6], nil)
		matchBlock(t, blocks[2], insns[6:7], nil)
		matchBlock(t, blocks[3], insns[7:], nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestDivisionByZeroParentsNOK(t *testing.T) {
	test := func(t *testing.T, op bpf.ALUOp) {
		t.Helper()
		insns := toInstructions([]bpf.Instruction{
			  bpf.LoadAbsolute{Size: 1, Off: 0},
			  bpf.TXA{},
			  bpf.ALUOpX{Op: op},
			  bpf.JumpIf{Cond: bpf.JumpEqual, Val: 3, SkipTrue: 0, SkipFalse: 2},  
			  bpf.LoadMemShift{Off: 1},  
			  bpf.Jump{Skip: 1},  
			  bpf.LoadAbsolute{Size: 1, Off: 2},
			  bpf.ALUOpX{Op: op},
			  bpf.RetConstant{},
		})
		blocks := mustSplitBlocks(t, 4, insns)
		err := addDivideByZeroGuards(blocks)
		if err != nil {
			t.Fatal(err)
		}
		matchBlock(t, blocks[0], join(
			insns[:2],
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[2:4],
		), nil)
		matchBlock(t, blocks[1], insns[4:6], nil)
		matchBlock(t, blocks[2], insns[6:7], nil)
		matchBlock(t, blocks[3], join(
			[]instruction{{Instruction: checkXNotZero{}}},
			insns[7:],
		), nil)
	}
	test(t, bpf.ALUOpDiv)
	test(t, bpf.ALUOpMod)
}
func TestAbsoluteGuardSize(t *testing.T) {
	insns := toInstructions([]bpf.Instruction{
		bpf.LoadAbsolute{Size: 4, Off: 10},  
		bpf.LoadAbsolute{Size: 1, Off: 10},  
		bpf.RetConstant{},
	})
	blocks := mustSplitBlocks(t, 1, insns)
	addAbsolutePacketGuards(blocks)
	matchBlock(t, blocks[0], join(
		[]instruction{{Instruction: packetGuardAbsolute{guard: 14}}},
		insns,
	), nil)
}