From f5b919f42c109b84866b1198a98e6448de96f585 Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Tue, 15 Oct 2024 17:16:03 +0200 Subject: [PATCH 1/2] compiler: support resources in conditional fields For now, resources are only allowed if their base type is one of constant, integer, or flag. This is particularly useful when combined with syz_create_resource (cf. subsequent commits). Signed-off-by: Paul Chaignon --- pkg/compiler/check.go | 6 +++++- pkg/compiler/testdata/errors2.txt | 2 +- prog/expr.go | 16 +++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pkg/compiler/check.go b/pkg/compiler/check.go index 87da67c391d0..be81114cde0e 100644 --- a/pkg/compiler/check.go +++ b/pkg/compiler/check.go @@ -649,7 +649,11 @@ func (comp *compiler) checkPathField(target, t *ast.Type, field *ast.Field) bool } func (comp *compiler) checkExprLastField(target *ast.Type, field *ast.Field) { - _, desc := comp.derefPointers(field.Type) + typ, desc := comp.derefPointers(field.Type) + if desc == typeResource { + r := comp.resources[typ.Ident] + desc = comp.getTypeDesc(r.Base) + } if desc != typeInt && desc != typeFlags && desc != typeConst { comp.error(target.Pos, "%v does not refer to a constant, an integer, or a flag", field.Name.Name) } diff --git a/pkg/compiler/testdata/errors2.txt b/pkg/compiler/testdata/errors2.txt index 71e2aa5ad27a..c433a7617c2e 100644 --- a/pkg/compiler/testdata/errors2.txt +++ b/pkg/compiler/testdata/errors2.txt @@ -470,7 +470,7 @@ conditional_fields { f3 some_nested_flags f4 int32 (if[value[f3:f1] != 0]) f5 int32 (if[value[f3:f2:f4] != 0]) ### value path f2 does not refer to a struct - f6 int32 (if[value[f3:f4] != 0]) ### f4 does not refer to a constant, an integer, or a flag + f6 int32 (if[value[f3:f4] != 0]) f7 int32 (if[value[f3:some_field] != 0]) ### value target some_field does not exist in some_nested_flags f8 int32 (if[value[f3:f5] != 0]) ### f5 has conditions, so value path cannot reference it f9 int32 (if[value[parent:a] != 0]) ### value target a does not exist in conditional_fields diff --git a/prog/expr.go b/prog/expr.go index e9446ab2ca03..4cbbb375898a 100644 --- a/prog/expr.go +++ b/prog/expr.go @@ -46,11 +46,21 @@ func (v *Value) Evaluate(finder ArgFinder) (uint64, bool) { if found == nil { panic(fmt.Sprintf("no argument was found by %v", v.Path)) } - constArg, ok := found.(*ConstArg) - if !ok { + val := uint64(0) +iter: + switch arg := found.(type) { + case *ConstArg: + val = arg.Val + case *ResultArg: + if arg.Res != nil { + found = arg.Res + goto iter + } + val = arg.Val + default: panic("value expressions must only rely on int fields") } - return constArg.Val, true + return val, true } func makeArgFinder(t *Target, c *Call, unionArg *UnionArg, parents parentStack) ArgFinder { From 11da221a7bf4dd135c60c6073ac146c295ce979e Mon Sep 17 00:00:00 2001 From: Paul Chaignon Date: Tue, 15 Oct 2024 17:19:18 +0200 Subject: [PATCH 2/2] sys/linux: describe BPF helper bpf_map_lookup_elem The size of BPF map keys (and values) are fixed at map creation time. In the descriptions, we must therefore ensure that the size declared when creating the map matches the size used during the map lookup in the BPF program. To that end, we create a resource from the map key size with syz_create_resource$map_key_size. This resource is then used in both bpf$MAP_CREATE and bpf$PROG_LOAD. More specifically, it is used in two places in the loaded BPF program, as shown below. 1: r1 = map[id:10] 2: r8 = X 3: *(u64 *)(r10 -8) = r8 4: r2 = r10 5: r2 -= 8 6: call bpf_map_lookup_elem The key size resource is used on instruction 3 to determine the size of the store instruction on the stack, and on instruction 5 to set the corresponding offset to the stack. For instruction 5, the resource can directly be used. For instruction 3 however, the resource is used in a conditional fields as the instruction itself will change depending on the key size. Note that the description only supports four key sizes currently (8, 16, 32, and 64 bits). Supporting more sizes would be a more complex, with a variable number of store instructions needed. In addition, nothing in the description forces the map_key_size used at map creation to be the same as the map_key_size used in the program. That being said, in a given generated program, there will likely be only so many such resources, so the likelihood of the two resources being the same is high. Signed-off-by: Paul Chaignon --- sys/linux/bpf.txt | 7 +++++-- sys/linux/bpf_prog.txt | 27 +++++++++++++++++++++++++++ sys/linux/bpf_prog.txt.const | 1 + sys/linux/test/bpf_helpers_map | 8 ++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 sys/linux/test/bpf_helpers_map diff --git a/sys/linux/bpf.txt b/sys/linux/bpf.txt index 2f4a7bd50628..523ec6fc3d2d 100644 --- a/sys/linux/bpf.txt +++ b/sys/linux/bpf.txt @@ -26,6 +26,7 @@ resource bpf_frozen_const_str[fd_bpf_const_str] resource tail_call_map_fd[fd_bpf_map] resource tail_call_map[tail_call_map_fd] resource ringbuf_map_fd[fd_bpf_map] +resource map_key_size[int32]: 1, 2, 4, 8 # NEED: this is a random index in btf_header:types. We can't express this, so we just use a small index. type btf_type_id int32[1:5] @@ -99,6 +100,8 @@ bpf_map_const_str_freeze { out bpf_frozen_const_str (out_overlay) } +syz_create_resource$map_key_size(ksize int32) map_key_size + bpf_map_create_arg [ base bpf_map_create_arg_base bloom_filter bpf_map_create_arg_bf @@ -126,8 +129,8 @@ type bpf_map_create_arg_t[TYPE, KSIZE, VSIZE, MAX, FLAGS, MAP_EXTRA] { pad2 const[0, int32] (if[value[flags] & BPF_F_TOKEN_FD == 0]) } [packed] -type bpf_map_create_arg_base bpf_map_create_arg_t[flags[bpf_map_type, int32], int32, int32, int32, flags[map_flags, int32], const[0, int64]] -type bpf_map_create_arg_bf bpf_map_create_arg_t[const[BPF_MAP_TYPE_BLOOM_FILTER, int32], int32, int32, int32, flags[map_flags, int32], int64[0:15]] +type bpf_map_create_arg_base bpf_map_create_arg_t[flags[bpf_map_type, int32], map_key_size, int32, int32, flags[map_flags, int32], const[0, int64]] +type bpf_map_create_arg_bf bpf_map_create_arg_t[const[BPF_MAP_TYPE_BLOOM_FILTER, int32], map_key_size, int32, int32, flags[map_flags, int32], int64[0:15]] type bpf_map_create_arg_ringbuf bpf_map_create_arg_t[const[BPF_MAP_TYPE_RINGBUF, int32], const[0, int32], const[0, int32], int32, const[0, int32], const[0, int64]] type bpf_map_create_arg_const_str bpf_map_create_arg_t[const[BPF_MAP_TYPE_ARRAY, int32], const[4, int32], const[8, int32], const[1, int32], const[BPF_F_RDONLY_PROG, int32], const[0, int64]] type bpf_map_create_arg_tail_call bpf_map_create_arg_t[const[BPF_MAP_TYPE_PROG_ARRAY, int32], const[4, int32], const[4, int32], const[10, int32], const[0, int32], const[0, int64]] diff --git a/sys/linux/bpf_prog.txt b/sys/linux/bpf_prog.txt index 82ae0b217e3f..99529801be63 100644 --- a/sys/linux/bpf_prog.txt +++ b/sys/linux/bpf_prog.txt @@ -150,6 +150,7 @@ bpf_insn [ tail_call bpf_insn_tail_call ringbuf_query bpf_insn_ringbuf_query ringbuf_output bpf_insn_ringbuf_output + map_lookup bpf_insn_map_lookup ] [varlen] bpf_insn_generic { @@ -171,6 +172,9 @@ type bpf_insn_ldst_t[CLASS, SZ, MODE, DST, SRC, OFF, IMM] { } type bpf_insn_ldst bpf_insn_ldst_t[bpf_ldst_insn, bpf_ldst_size, bpf_ldst_mode, flags[bpf_reg, int8:4], flags[bpf_reg, int8:4], flags[bpf_insn_offsets, int16], flags[bpf_insn_immediates, int32]] +type bpf_insn_st8_reg[SRC, DST, OFF] bpf_insn_ldst_t[BPF_STX, BPF_B0, BPF_MEM0, const[DST, int8:4], const[SRC, int8:4], const[OFF, int16], const[0, int32]] +type bpf_insn_st16_reg[SRC, DST, OFF] bpf_insn_ldst_t[BPF_STX, BPF_H0, BPF_MEM0, const[DST, int8:4], const[SRC, int8:4], const[OFF, int16], const[0, int32]] +type bpf_insn_st32_reg[SRC, DST, OFF] bpf_insn_ldst_t[BPF_STX, BPF_W0, BPF_MEM0, const[DST, int8:4], const[SRC, int8:4], const[OFF, int16], const[0, int32]] type bpf_insn_st64_reg[SRC, DST, OFF] bpf_insn_ldst_t[BPF_STX, BPF_DW0, BPF_MEM0, const[DST, int8:4], const[SRC, int8:4], const[OFF, int16], const[0, int32]] bpf_ldst_insn = BPF_LD, BPF_LDX, BPF_ST, BPF_STX @@ -555,3 +559,26 @@ bpf_insn_ringbuf_output { define MAX_BPF_REG __MAX_BPF_REG bpf_ringbuf_wakeup_flags = 0, BPF_RB_NO_WAKEUP, BPF_RB_FORCE_WAKEUP + +# (18) r1 = map[id:10] +# (b7) r8 = X +# (7b) *(u64 *)(r10 -8) = r8 +# (bf) r2 = r10 +# (07) r2 -= 8 +# (85) call bpf_map_lookup_elem +bpf_insn_map_lookup { + insn1 bpf_insn_map_fd_t[const[BPF_REG_1, int8:4], fd_bpf_map] + insn2 bpf_insn_mov_imm_any[BPF_REG_8] + insn3 bpf_insn_stX_reg + insn4 bpf_insn_mov_reg[BPF_REG_10, BPF_REG_2] + insn5 bpf_insn_alu_t[BPF_ALU64, BPF_K0, BPF_SUB0, const[BPF_REG_2, int8:4], const[0, int8:4], const[0, int16], map_key_size] + insn6 bpf_insn_call_helper_t[const[BPF_FUNC_map_lookup_elem, int32]] +} [packed] + +bpf_insn_stX_reg [ + st8 bpf_insn_st8_reg[BPF_REG_8, BPF_REG_10, -1] (if[value[parent:parent:insn5:imm] == 1]) + st16 bpf_insn_st16_reg[BPF_REG_8, BPF_REG_10, -2] (if[value[parent:parent:insn5:imm] == 2]) + st32 bpf_insn_st32_reg[BPF_REG_8, BPF_REG_10, -4] (if[value[parent:parent:insn5:imm] == 4]) + st64 bpf_insn_st64_reg[BPF_REG_8, BPF_REG_10, -8] (if[value[parent:parent:insn5:imm] == 8]) + fallback bpf_insn_st64_reg[BPF_REG_8, BPF_REG_10, -8] +] diff --git a/sys/linux/bpf_prog.txt.const b/sys/linux/bpf_prog.txt.const index 7689bdd2415e..21bd6f4faa09 100644 --- a/sys/linux/bpf_prog.txt.const +++ b/sys/linux/bpf_prog.txt.const @@ -43,6 +43,7 @@ BPF_END0 = 13 BPF_EXIT0 = 9 BPF_FLOW_DISSECTOR = 17 BPF_FUNC_INFO_SIZE = 8 +BPF_FUNC_map_lookup_elem = 1 BPF_FUNC_ringbuf_discard = 133 BPF_FUNC_ringbuf_output = 130 BPF_FUNC_ringbuf_query = 134 diff --git a/sys/linux/test/bpf_helpers_map b/sys/linux/test/bpf_helpers_map new file mode 100644 index 000000000000..d3a5f32ac5e8 --- /dev/null +++ b/sys/linux/test/bpf_helpers_map @@ -0,0 +1,8 @@ +# Create a BPF map + +r0 = syz_create_resource$map_key_size(0x4) +r1 = bpf$MAP_CREATE(AUTO, &AUTO=@base={0x1, r0, 0x8, 0x1, 0x0, 0x0, 0x0, "00000000000000000000000000000000", 0x0, 0x0, 0x0, 0x0, 0x0, AUTO, @void, @value=AUTO, @void, @value=AUTO}, 0x48) + +# Lookup an element from the map + +r2 = bpf$PROG_LOAD(AUTO, &AUTO={0x3, AUTO, &AUTO=@framed={{AUTO, AUTO, AUTO, AUTO, 0x0, AUTO, AUTO, AUTO, 0x0}, [@map_lookup={{AUTO, AUTO, AUTO, AUTO, r1, AUTO, AUTO, AUTO, AUTO}, {AUTO, AUTO, AUTO, AUTO, AUTO, AUTO, 0x1234}, @st32=AUTO, AUTO, {AUTO, AUTO, AUTO, AUTO, AUTO, AUTO, r0}, AUTO}], AUTO}, &AUTO='GPL\x00', 0x0, 0x0, 0x0, 0x0, 0x0, "00000000000000000000000000000000", 0x0, @sched_cls=0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @void, @value=AUTO}, 0xa0)