| From foo@baz Wed Sep 15 02:37:47 PM CEST 2021 |
| From: Ovidiu Panait <ovidiu.panait@windriver.com> |
| Date: Mon, 13 Sep 2021 18:35:31 +0300 |
| Subject: selftests/bpf: Test variable offset stack access |
| To: stable@vger.kernel.org |
| Cc: bpf@vger.kernel.org, daniel@iogearbox.net |
| Message-ID: <20210913153537.2162465-8-ovidiu.panait@windriver.com> |
| |
| From: Andrey Ignatov <rdna@fb.com> |
| |
| commit 8ff80e96e3ccea5ff0a890d4f18997e0344dbec2 upstream. |
| |
| Test different scenarios of indirect variable-offset stack access: out of |
| bound access (>0), min_off below initialized part of the stack, |
| max_off+size above initialized part of the stack, initialized stack. |
| |
| Example of output: |
| ... |
| #856/p indirect variable-offset stack access, out of bound OK |
| #857/p indirect variable-offset stack access, max_off+size > max_initialized OK |
| #858/p indirect variable-offset stack access, min_off < min_initialized OK |
| #859/p indirect variable-offset stack access, ok OK |
| ... |
| |
| Signed-off-by: Andrey Ignatov <rdna@fb.com> |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| [OP: backport to 4.19] |
| Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| tools/testing/selftests/bpf/test_verifier.c | 79 +++++++++++++++++++++++++++- |
| 1 file changed, 77 insertions(+), 2 deletions(-) |
| |
| --- a/tools/testing/selftests/bpf/test_verifier.c |
| +++ b/tools/testing/selftests/bpf/test_verifier.c |
| @@ -8495,7 +8495,7 @@ static struct bpf_test tests[] = { |
| .prog_type = BPF_PROG_TYPE_LWT_IN, |
| }, |
| { |
| - "indirect variable-offset stack access", |
| + "indirect variable-offset stack access, out of bound", |
| .insns = { |
| /* Fill the top 8 bytes of the stack */ |
| BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), |
| @@ -8516,11 +8516,86 @@ static struct bpf_test tests[] = { |
| BPF_EXIT_INSN(), |
| }, |
| .fixup_map1 = { 5 }, |
| - .errstr = "variable stack read R2", |
| + .errstr = "invalid stack type R2 var_off", |
| .result = REJECT, |
| .prog_type = BPF_PROG_TYPE_LWT_IN, |
| }, |
| { |
| + "indirect variable-offset stack access, max_off+size > max_initialized", |
| + .insns = { |
| + /* Fill only the second from top 8 bytes of the stack. */ |
| + BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0), |
| + /* Get an unknown value. */ |
| + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), |
| + /* Make it small and 4-byte aligned. */ |
| + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4), |
| + BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16), |
| + /* Add it to fp. We now have either fp-12 or fp-16, but we don't know |
| + * which. fp-12 size 8 is partially uninitialized stack. |
| + */ |
| + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10), |
| + /* Dereference it indirectly. */ |
| + BPF_LD_MAP_FD(BPF_REG_1, 0), |
| + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), |
| + BPF_MOV64_IMM(BPF_REG_0, 0), |
| + BPF_EXIT_INSN(), |
| + }, |
| + .fixup_map1 = { 5 }, |
| + .errstr = "invalid indirect read from stack var_off", |
| + .result = REJECT, |
| + .prog_type = BPF_PROG_TYPE_LWT_IN, |
| + }, |
| + { |
| + "indirect variable-offset stack access, min_off < min_initialized", |
| + .insns = { |
| + /* Fill only the top 8 bytes of the stack. */ |
| + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), |
| + /* Get an unknown value */ |
| + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), |
| + /* Make it small and 4-byte aligned. */ |
| + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4), |
| + BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16), |
| + /* Add it to fp. We now have either fp-12 or fp-16, but we don't know |
| + * which. fp-16 size 8 is partially uninitialized stack. |
| + */ |
| + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10), |
| + /* Dereference it indirectly. */ |
| + BPF_LD_MAP_FD(BPF_REG_1, 0), |
| + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), |
| + BPF_MOV64_IMM(BPF_REG_0, 0), |
| + BPF_EXIT_INSN(), |
| + }, |
| + .fixup_map1 = { 5 }, |
| + .errstr = "invalid indirect read from stack var_off", |
| + .result = REJECT, |
| + .prog_type = BPF_PROG_TYPE_LWT_IN, |
| + }, |
| + { |
| + "indirect variable-offset stack access, ok", |
| + .insns = { |
| + /* Fill the top 16 bytes of the stack. */ |
| + BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, 0), |
| + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), |
| + /* Get an unknown value. */ |
| + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), |
| + /* Make it small and 4-byte aligned. */ |
| + BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4), |
| + BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 16), |
| + /* Add it to fp. We now have either fp-12 or fp-16, we don't know |
| + * which, but either way it points to initialized stack. |
| + */ |
| + BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10), |
| + /* Dereference it indirectly. */ |
| + BPF_LD_MAP_FD(BPF_REG_1, 0), |
| + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), |
| + BPF_MOV64_IMM(BPF_REG_0, 0), |
| + BPF_EXIT_INSN(), |
| + }, |
| + .fixup_map1 = { 6 }, |
| + .result = ACCEPT, |
| + .prog_type = BPF_PROG_TYPE_LWT_IN, |
| + }, |
| + { |
| "direct stack access with 32-bit wraparound. test1", |
| .insns = { |
| BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), |