mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
Allow to search for expected register state in all the verifier log output that's related to specified instruction number. See added comment for an example of possible situation that is happening due to a simple enhancement done in the next patch, which fixes handling of env->test_state_freq flag in state checkpointing logic. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20230302235015.2044271-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
702 lines
23 KiB
C
702 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <test_progs.h>
|
|
|
|
#define MAX_INSNS 512
|
|
#define MAX_MATCHES 24
|
|
|
|
struct bpf_reg_match {
|
|
unsigned int line;
|
|
const char *match;
|
|
};
|
|
|
|
struct bpf_align_test {
|
|
const char *descr;
|
|
struct bpf_insn insns[MAX_INSNS];
|
|
enum {
|
|
UNDEF,
|
|
ACCEPT,
|
|
REJECT
|
|
} result;
|
|
enum bpf_prog_type prog_type;
|
|
/* Matches must be in order of increasing line */
|
|
struct bpf_reg_match matches[MAX_MATCHES];
|
|
};
|
|
|
|
static struct bpf_align_test tests[] = {
|
|
/* Four tests of known constants. These aren't staggeringly
|
|
* interesting since we track exact values now.
|
|
*/
|
|
{
|
|
.descr = "mov",
|
|
.insns = {
|
|
BPF_MOV64_IMM(BPF_REG_3, 2),
|
|
BPF_MOV64_IMM(BPF_REG_3, 4),
|
|
BPF_MOV64_IMM(BPF_REG_3, 8),
|
|
BPF_MOV64_IMM(BPF_REG_3, 16),
|
|
BPF_MOV64_IMM(BPF_REG_3, 32),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{0, "R1=ctx(off=0,imm=0)"},
|
|
{0, "R10=fp0"},
|
|
{0, "R3_w=2"},
|
|
{1, "R3_w=4"},
|
|
{2, "R3_w=8"},
|
|
{3, "R3_w=16"},
|
|
{4, "R3_w=32"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "shift",
|
|
.insns = {
|
|
BPF_MOV64_IMM(BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_3, 4),
|
|
BPF_MOV64_IMM(BPF_REG_4, 32),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{0, "R1=ctx(off=0,imm=0)"},
|
|
{0, "R10=fp0"},
|
|
{0, "R3_w=1"},
|
|
{1, "R3_w=2"},
|
|
{2, "R3_w=4"},
|
|
{3, "R3_w=8"},
|
|
{4, "R3_w=16"},
|
|
{5, "R3_w=1"},
|
|
{6, "R4_w=32"},
|
|
{7, "R4_w=16"},
|
|
{8, "R4_w=8"},
|
|
{9, "R4_w=4"},
|
|
{10, "R4_w=2"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "addsub",
|
|
.insns = {
|
|
BPF_MOV64_IMM(BPF_REG_3, 4),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 4),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 2),
|
|
BPF_MOV64_IMM(BPF_REG_4, 8),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 2),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{0, "R1=ctx(off=0,imm=0)"},
|
|
{0, "R10=fp0"},
|
|
{0, "R3_w=4"},
|
|
{1, "R3_w=8"},
|
|
{2, "R3_w=10"},
|
|
{3, "R4_w=8"},
|
|
{4, "R4_w=12"},
|
|
{5, "R4_w=14"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "mul",
|
|
.insns = {
|
|
BPF_MOV64_IMM(BPF_REG_3, 7),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 2),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 4),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{0, "R1=ctx(off=0,imm=0)"},
|
|
{0, "R10=fp0"},
|
|
{0, "R3_w=7"},
|
|
{1, "R3_w=7"},
|
|
{2, "R3_w=14"},
|
|
{3, "R3_w=56"},
|
|
},
|
|
},
|
|
|
|
/* Tests using unknown values */
|
|
#define PREP_PKT_POINTERS \
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, \
|
|
offsetof(struct __sk_buff, data)), \
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, \
|
|
offsetof(struct __sk_buff, data_end))
|
|
|
|
#define LOAD_UNKNOWN(DST_REG) \
|
|
PREP_PKT_POINTERS, \
|
|
BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), \
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), \
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 1), \
|
|
BPF_EXIT_INSN(), \
|
|
BPF_LDX_MEM(BPF_B, DST_REG, BPF_REG_2, 0)
|
|
|
|
{
|
|
.descr = "unknown shift",
|
|
.insns = {
|
|
LOAD_UNKNOWN(BPF_REG_3),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
|
|
LOAD_UNKNOWN(BPF_REG_4),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 5),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{6, "R0_w=pkt(off=8,r=8,imm=0)"},
|
|
{6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
|
|
{7, "R3_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
|
|
{8, "R3_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{9, "R3_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
|
|
{10, "R3_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
|
|
{12, "R3_w=pkt_end(off=0,imm=0)"},
|
|
{17, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
|
|
{18, "R4_w=scalar(umax=8160,var_off=(0x0; 0x1fe0))"},
|
|
{19, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
|
|
{20, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
|
|
{21, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{22, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "unknown mul",
|
|
.insns = {
|
|
LOAD_UNKNOWN(BPF_REG_3),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 1),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 4),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 8),
|
|
BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
|
|
{7, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
|
|
{8, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
|
|
{9, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
|
|
{10, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
|
|
{11, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
|
|
{12, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{13, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
|
|
{14, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
|
|
{15, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "packet const offset",
|
|
.insns = {
|
|
PREP_PKT_POINTERS,
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
|
|
/* Skip over ethernet header. */
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
|
|
BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 0),
|
|
BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 1),
|
|
BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 2),
|
|
BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 3),
|
|
BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 0),
|
|
BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 2),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
|
|
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
{2, "R5_w=pkt(off=0,r=0,imm=0)"},
|
|
{4, "R5_w=pkt(off=14,r=0,imm=0)"},
|
|
{5, "R4_w=pkt(off=14,r=0,imm=0)"},
|
|
{9, "R2=pkt(off=0,r=18,imm=0)"},
|
|
{10, "R5=pkt(off=14,r=18,imm=0)"},
|
|
{10, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
|
|
{13, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
|
|
{14, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "packet variable offset",
|
|
.insns = {
|
|
LOAD_UNKNOWN(BPF_REG_6),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
|
|
|
|
/* First, add a constant to the R5 packet pointer,
|
|
* then a variable with a known alignment.
|
|
*/
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
|
|
|
|
/* Now, test in the other direction. Adding first
|
|
* the variable offset to R5, then the constant.
|
|
*/
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
|
|
|
|
/* Test multiple accumulations of unknown values
|
|
* into a packet pointer.
|
|
*/
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
|
|
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
{6, "R2_w=pkt(off=0,r=8,imm=0)"},
|
|
{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Offset is added to packet pointer R5, resulting in
|
|
* known fixed offset, and variable offset from R6.
|
|
*/
|
|
{11, "R5_w=pkt(id=1,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* At the time the word size load is performed from R5,
|
|
* it's total offset is NET_IP_ALIGN + reg->off (0) +
|
|
* reg->aux_off (14) which is 16. Then the variable
|
|
* offset is considered using reg->aux_off_align which
|
|
* is 4 and meets the load's requirements.
|
|
*/
|
|
{15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Variable offset is added to R5 packet pointer,
|
|
* resulting in auxiliary alignment of 4. To avoid BPF
|
|
* verifier's precision backtracking logging
|
|
* interfering we also have a no-op R4 = R5
|
|
* instruction to validate R5 state. We also check
|
|
* that R4 is what it should be in such case.
|
|
*/
|
|
{18, "R4_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{18, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Constant offset is added to R5, resulting in
|
|
* reg->off of 14.
|
|
*/
|
|
{19, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off
|
|
* (14) which is 16. Then the variable offset is 4-byte
|
|
* aligned, so the total offset is 4-byte aligned and
|
|
* meets the load's requirements.
|
|
*/
|
|
{24, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{24, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Constant offset is added to R5 packet pointer,
|
|
* resulting in reg->off value of 14.
|
|
*/
|
|
{26, "R5_w=pkt(off=14,r=8"},
|
|
/* Variable offset is added to R5, resulting in a
|
|
* variable offset of (4n). See comment for insn #18
|
|
* for R4 = R5 trick.
|
|
*/
|
|
{28, "R4_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
{28, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Constant is added to R5 again, setting reg->off to 18. */
|
|
{29, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* And once more we add a variable; resulting var_off
|
|
* is still (4n), fixed offset is not changed.
|
|
* Also, we create a new reg->id.
|
|
*/
|
|
{31, "R4_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
|
|
{31, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (18)
|
|
* which is 20. Then the variable offset is (4n), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
{35, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
|
|
{35, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "packet variable offset 2",
|
|
.insns = {
|
|
/* Create an unknown offset, (4n+2)-aligned */
|
|
LOAD_UNKNOWN(BPF_REG_6),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
|
|
/* Add it to the packet pointer */
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
/* Check bounds and perform a read */
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
|
|
/* Make a (4n) offset from the value we just read */
|
|
BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
|
|
/* Add it to the packet pointer */
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
/* Check bounds and perform a read */
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
{6, "R2_w=pkt(off=0,r=8,imm=0)"},
|
|
{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
{8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
|
|
/* Packet pointer has (4n+2) offset */
|
|
{11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
|
|
{12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
{15, "R5=pkt(id=1,off=0,r=4,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
|
|
/* Newly read value in R6 was shifted left by 2, so has
|
|
* known alignment of 4.
|
|
*/
|
|
{17, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Added (4n) to packet pointer's (4n+2) var_off, giving
|
|
* another (4n+2).
|
|
*/
|
|
{19, "R5_w=pkt(id=2,off=0,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
|
|
{20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
{23, "R5=pkt(id=2,off=0,r=4,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
|
|
},
|
|
},
|
|
{
|
|
.descr = "dubious pointer arithmetic",
|
|
.insns = {
|
|
PREP_PKT_POINTERS,
|
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
|
/* (ptr - ptr) << 2 */
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
|
|
BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
|
|
/* We have a (4n) value. Let's make a packet offset
|
|
* out of it. First add 14, to make it a (4n+2)
|
|
*/
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
|
|
/* Then make sure it's nonnegative */
|
|
BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1),
|
|
BPF_EXIT_INSN(),
|
|
/* Add it to packet pointer */
|
|
BPF_MOV64_REG(BPF_REG_6, BPF_REG_2),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5),
|
|
/* Check bounds and perform a read */
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_6),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.result = REJECT,
|
|
.matches = {
|
|
{3, "R5_w=pkt_end(off=0,imm=0)"},
|
|
/* (ptr - ptr) << 2 == unknown, (4n) */
|
|
{5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"},
|
|
/* (4n) + 14 == (4n+2). We blow our bounds, because
|
|
* the add could overflow.
|
|
*/
|
|
{6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
|
|
/* Checked s>=0 */
|
|
{9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
|
|
/* packet pointer + nonnegative (4n+2) */
|
|
{11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
|
|
{12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
|
|
/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
|
|
* We checked the bounds, but it might have been able
|
|
* to overflow if the packet pointer started in the
|
|
* upper half of the address space.
|
|
* So we did not get a 'range' on R6, and the access
|
|
* attempt will fail.
|
|
*/
|
|
{15, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
|
|
}
|
|
},
|
|
{
|
|
.descr = "variable subtraction",
|
|
.insns = {
|
|
/* Create an unknown offset, (4n+2)-aligned */
|
|
LOAD_UNKNOWN(BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
|
|
/* Create another unknown, (4n)-aligned, and subtract
|
|
* it from the first one
|
|
*/
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
|
|
BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7),
|
|
/* Bounds-check the result */
|
|
BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1),
|
|
BPF_EXIT_INSN(),
|
|
/* Add it to the packet pointer */
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
|
|
/* Check bounds and perform a read */
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
{6, "R2_w=pkt(off=0,r=8,imm=0)"},
|
|
{8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
{9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
|
|
/* New unknown value in R7 is (4n) */
|
|
{10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
|
|
/* Subtracting it from R6 blows our unsigned bounds */
|
|
{11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
|
|
/* Checked s>= 0 */
|
|
{14, "R6=scalar(umin=2,umax=1034,var_off=(0x2; 0x7fc))"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
{20, "R5=pkt(id=2,off=0,r=4,umin=2,umax=1034,var_off=(0x2; 0x7fc)"},
|
|
|
|
},
|
|
},
|
|
{
|
|
.descr = "pointer variable subtraction",
|
|
.insns = {
|
|
/* Create an unknown offset, (4n+2)-aligned and bounded
|
|
* to [14,74]
|
|
*/
|
|
LOAD_UNKNOWN(BPF_REG_6),
|
|
BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
|
|
BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xf),
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
|
|
/* Subtract it from the packet pointer */
|
|
BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
|
|
BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_6),
|
|
/* Create another unknown, (4n)-aligned and >= 74.
|
|
* That in fact means >= 76, since 74 % 4 == 2
|
|
*/
|
|
BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 76),
|
|
/* Add it to the packet pointer */
|
|
BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_7),
|
|
/* Check bounds and perform a read */
|
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
|
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
|
|
BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
|
|
BPF_EXIT_INSN(),
|
|
BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
|
.matches = {
|
|
/* Calculated offset in R6 has unknown value, but known
|
|
* alignment of 4.
|
|
*/
|
|
{6, "R2_w=pkt(off=0,r=8,imm=0)"},
|
|
{9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"},
|
|
/* Adding 14 makes R6 be (4n+2) */
|
|
{10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"},
|
|
/* Subtracting from packet pointer overflows ubounds */
|
|
{13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"},
|
|
/* New unknown value in R7 is (4n), >= 76 */
|
|
{14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"},
|
|
/* Adding it to packet pointer gives nice bounds again */
|
|
{16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
|
|
/* At the time the word size load is performed from R5,
|
|
* its total fixed offset is NET_IP_ALIGN + reg->off (0)
|
|
* which is 2. Then the variable offset is (4n+2), so
|
|
* the total offset is 4-byte aligned and meets the
|
|
* load's requirements.
|
|
*/
|
|
{20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
|
|
},
|
|
},
|
|
};
|
|
|
|
static int probe_filter_length(const struct bpf_insn *fp)
|
|
{
|
|
int len;
|
|
|
|
for (len = MAX_INSNS - 1; len > 0; --len)
|
|
if (fp[len].code != 0 || fp[len].imm != 0)
|
|
break;
|
|
return len + 1;
|
|
}
|
|
|
|
static char bpf_vlog[32768];
|
|
|
|
static int do_test_single(struct bpf_align_test *test)
|
|
{
|
|
struct bpf_insn *prog = test->insns;
|
|
int prog_type = test->prog_type;
|
|
char bpf_vlog_copy[32768];
|
|
LIBBPF_OPTS(bpf_prog_load_opts, opts,
|
|
.prog_flags = BPF_F_STRICT_ALIGNMENT,
|
|
.log_buf = bpf_vlog,
|
|
.log_size = sizeof(bpf_vlog),
|
|
.log_level = 2,
|
|
);
|
|
const char *line_ptr;
|
|
int cur_line = -1;
|
|
int prog_len, i;
|
|
int fd_prog;
|
|
int ret;
|
|
|
|
prog_len = probe_filter_length(prog);
|
|
fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
|
|
prog, prog_len, &opts);
|
|
if (fd_prog < 0 && test->result != REJECT) {
|
|
printf("Failed to load program.\n");
|
|
printf("%s", bpf_vlog);
|
|
ret = 1;
|
|
} else if (fd_prog >= 0 && test->result == REJECT) {
|
|
printf("Unexpected success to load!\n");
|
|
printf("%s", bpf_vlog);
|
|
ret = 1;
|
|
close(fd_prog);
|
|
} else {
|
|
ret = 0;
|
|
/* We make a local copy so that we can strtok() it */
|
|
strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
|
|
line_ptr = strtok(bpf_vlog_copy, "\n");
|
|
for (i = 0; i < MAX_MATCHES; i++) {
|
|
struct bpf_reg_match m = test->matches[i];
|
|
int tmp;
|
|
|
|
if (!m.match)
|
|
break;
|
|
while (line_ptr) {
|
|
cur_line = -1;
|
|
sscanf(line_ptr, "%u: ", &cur_line);
|
|
if (cur_line == -1)
|
|
sscanf(line_ptr, "from %u to %u: ", &tmp, &cur_line);
|
|
if (cur_line == m.line)
|
|
break;
|
|
line_ptr = strtok(NULL, "\n");
|
|
}
|
|
if (!line_ptr) {
|
|
printf("Failed to find line %u for match: %s\n",
|
|
m.line, m.match);
|
|
ret = 1;
|
|
printf("%s", bpf_vlog);
|
|
break;
|
|
}
|
|
/* Check the next line as well in case the previous line
|
|
* did not have a corresponding bpf insn. Example:
|
|
* func#0 @0
|
|
* 0: R1=ctx(off=0,imm=0) R10=fp0
|
|
* 0: (b7) r3 = 2 ; R3_w=2
|
|
*
|
|
* Sometimes it's actually two lines below, e.g. when
|
|
* searching for "6: R3_w=scalar(umax=255,var_off=(0x0; 0xff))":
|
|
* from 4 to 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
|
|
* 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
|
|
* 6: (71) r3 = *(u8 *)(r2 +0) ; R2_w=pkt(off=0,r=8,imm=0) R3_w=scalar(umax=255,var_off=(0x0; 0xff))
|
|
*/
|
|
while (!strstr(line_ptr, m.match)) {
|
|
cur_line = -1;
|
|
line_ptr = strtok(NULL, "\n");
|
|
sscanf(line_ptr ?: "", "%u: ", &cur_line);
|
|
if (!line_ptr || cur_line != m.line)
|
|
break;
|
|
}
|
|
if (cur_line != m.line || !line_ptr || !strstr(line_ptr, m.match)) {
|
|
printf("Failed to find match %u: %s\n", m.line, m.match);
|
|
ret = 1;
|
|
printf("%s", bpf_vlog);
|
|
break;
|
|
}
|
|
}
|
|
if (fd_prog >= 0)
|
|
close(fd_prog);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void test_align(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
|
struct bpf_align_test *test = &tests[i];
|
|
|
|
if (!test__start_subtest(test->descr))
|
|
continue;
|
|
|
|
ASSERT_OK(do_test_single(test), test->descr);
|
|
}
|
|
}
|