#include <stddef.h>
 #include <stdbool.h>
 #include <sched.h>
+#include <limits.h>
 
 #include <sys/capability.h>
 #include <sys/resource.h>
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
-               .retval = 0,
+               .retval = 42,
        },
        {
                "DIV32 by 0, zero check 2",
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
-               .retval = 0,
+               .retval = 42,
        },
        {
                "DIV64 by 0, zero check",
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
-               .retval = 0,
+               .retval = 42,
        },
        {
                "MOD32 by 0, zero check 1",
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
-               .retval = 0,
+               .retval = 42,
        },
        {
                "MOD32 by 0, zero check 2",
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
-               .retval = 0,
+               .retval = 42,
        },
        {
                "MOD64 by 0, zero check",
                        BPF_EXIT_INSN(),
                },
                .result = ACCEPT,
+               .retval = 42,
+       },
+       {
+               "DIV32 by 0, zero check ok, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_0, 42),
+                       BPF_MOV32_IMM(BPF_REG_1, 2),
+                       BPF_MOV32_IMM(BPF_REG_2, 16),
+                       BPF_ALU32_REG(BPF_DIV, BPF_REG_2, BPF_REG_1),
+                       BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 8,
+       },
+       {
+               "DIV32 by 0, zero check 1, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, 0),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "DIV32 by 0, zero check 2, cls",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "DIV64 by 0, zero check, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, 0),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_ALU64_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "MOD32 by 0, zero check ok, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_0, 42),
+                       BPF_MOV32_IMM(BPF_REG_1, 3),
+                       BPF_MOV32_IMM(BPF_REG_2, 5),
+                       BPF_ALU32_REG(BPF_MOD, BPF_REG_2, BPF_REG_1),
+                       BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 2,
+       },
+       {
+               "MOD32 by 0, zero check 1, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, 0),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "MOD32 by 0, zero check 2, cls",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_1, 0xffffffff00000000LL),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "MOD64 by 0, zero check 1, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, 0),
+                       BPF_MOV32_IMM(BPF_REG_0, 2),
+                       BPF_ALU64_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 2,
+       },
+       {
+               "MOD64 by 0, zero check 2, cls",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, 0),
+                       BPF_MOV32_IMM(BPF_REG_0, -1),
+                       BPF_ALU64_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = -1,
+       },
+       /* Just make sure that JITs used udiv/umod as otherwise we get
+        * an exception from INT_MIN/-1 overflow similarly as with div
+        * by zero.
+        */
+       {
+               "DIV32 overflow, check 1",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, -1),
+                       BPF_MOV32_IMM(BPF_REG_0, INT_MIN),
+                       BPF_ALU32_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "DIV32 overflow, check 2",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_0, INT_MIN),
+                       BPF_ALU32_IMM(BPF_DIV, BPF_REG_0, -1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "DIV64 overflow, check 1",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_1, -1),
+                       BPF_LD_IMM64(BPF_REG_0, LLONG_MIN),
+                       BPF_ALU64_REG(BPF_DIV, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 0,
+       },
+       {
+               "DIV64 overflow, check 2",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_0, LLONG_MIN),
+                       BPF_ALU64_IMM(BPF_DIV, BPF_REG_0, -1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
                .retval = 0,
        },
+       {
+               "MOD32 overflow, check 1",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_1, -1),
+                       BPF_MOV32_IMM(BPF_REG_0, INT_MIN),
+                       BPF_ALU32_REG(BPF_MOD, BPF_REG_0, BPF_REG_1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = INT_MIN,
+       },
+       {
+               "MOD32 overflow, check 2",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_0, INT_MIN),
+                       BPF_ALU32_IMM(BPF_MOD, BPF_REG_0, -1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = INT_MIN,
+       },
+       {
+               "MOD64 overflow, check 1",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_1, -1),
+                       BPF_LD_IMM64(BPF_REG_2, LLONG_MIN),
+                       BPF_MOV64_REG(BPF_REG_3, BPF_REG_2),
+                       BPF_ALU64_REG(BPF_MOD, BPF_REG_2, BPF_REG_1),
+                       BPF_MOV32_IMM(BPF_REG_0, 0),
+                       BPF_JMP_REG(BPF_JNE, BPF_REG_3, BPF_REG_2, 1),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "MOD64 overflow, check 2",
+               .insns = {
+                       BPF_LD_IMM64(BPF_REG_2, LLONG_MIN),
+                       BPF_MOV64_REG(BPF_REG_3, BPF_REG_2),
+                       BPF_ALU64_IMM(BPF_MOD, BPF_REG_2, -1),
+                       BPF_MOV32_IMM(BPF_REG_0, 0),
+                       BPF_JMP_REG(BPF_JNE, BPF_REG_3, BPF_REG_2, 1),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "xor32 zero extend check",
+               .insns = {
+                       BPF_MOV32_IMM(BPF_REG_2, -1),
+                       BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 32),
+                       BPF_ALU64_IMM(BPF_OR, BPF_REG_2, 0xffff),
+                       BPF_ALU32_REG(BPF_XOR, BPF_REG_2, BPF_REG_2),
+                       BPF_MOV32_IMM(BPF_REG_0, 2),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0, 1),
+                       BPF_MOV32_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
        {
                "empty prog",
                .insns = {
                },
-               .errstr = "last insn is not an exit or jmp",
+               .errstr = "unknown opcode 00",
                .result = REJECT,
        },
        {
                        BPF_EXIT_INSN(),
                },
                .result = REJECT,
-               .errstr = "BPF_ARSH not supported for 32 bit ALU",
+               .errstr = "unknown opcode c4",
        },
        {
                "arsh32 on reg",
                        BPF_EXIT_INSN(),
                },
                .result = REJECT,
-               .errstr = "BPF_ARSH not supported for 32 bit ALU",
+               .errstr = "unknown opcode cc",
        },
        {
                "arsh64 on imm",
                        BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "BPF_CALL uses reserved",
+               .errstr = "unknown opcode 8d",
                .result = REJECT,
        },
        {
                        BPF_RAW_INSN(0, 0, 0, 0, 0),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid BPF_LD_IMM",
+               .errstr = "unknown opcode 00",
                .result = REJECT,
        },
        {
                        BPF_RAW_INSN(-1, 0, 0, 0, 0),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid BPF_ALU opcode f0",
+               .errstr = "unknown opcode ff",
                .result = REJECT,
        },
        {
                        BPF_RAW_INSN(-1, -1, -1, -1, -1),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "invalid BPF_ALU opcode f0",
+               .errstr = "unknown opcode ff",
                .result = REJECT,
        },
        {
                        },
                        BPF_EXIT_INSN(),
                },
-               .errstr = "BPF_END uses reserved fields",
+               .errstr = "unknown opcode d7",
                .result = REJECT,
        },
        {
                .result = ACCEPT,
                .retval = 1,
        },
+       {
+               "calls: div by 0 in subprog",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8),
+                       BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_MOV32_IMM(BPF_REG_2, 0),
+                       BPF_MOV32_IMM(BPF_REG_3, 1),
+                       BPF_ALU32_REG(BPF_DIV, BPF_REG_3, BPF_REG_2),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "calls: multiple ret types in subprog 1",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8),
+                       BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+                       BPF_MOV32_IMM(BPF_REG_0, 42),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = REJECT,
+               .errstr = "R0 invalid mem access 'inv'",
+       },
+       {
+               "calls: multiple ret types in subprog 2",
+               .insns = {
+                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 8),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_6),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8),
+                       BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 9),
+                       BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
+                       BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
+                       BPF_LD_MAP_FD(BPF_REG_1, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_map_lookup_elem),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 64),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .fixup_map1 = { 16 },
+               .result = REJECT,
+               .errstr = "R0 min value is outside of the array range",
+       },
        {
                "calls: overlapping caller/callee",
                .insns = {