2022-11-18 07:26:10 +05:30
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2023-08-08 19:27:55 +03:00
|
|
|
#include <regex.h>
|
2022-11-18 07:26:10 +05:30
|
|
|
#include <test_progs.h>
|
|
|
|
#include <network_helpers.h>
|
|
|
|
|
|
|
|
#include "test_spin_lock.skel.h"
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
#include "test_spin_lock_fail.skel.h"
|
|
|
|
|
|
|
|
static char log_buf[1024 * 1024];
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
const char *prog_name;
|
|
|
|
const char *err_msg;
|
|
|
|
} spin_lock_fail_tests[] = {
|
|
|
|
{ "lock_id_kptr_preserve",
|
|
|
|
"5: (bf) r1 = r0 ; R0_w=ptr_foo(id=2,ref_obj_id=2,off=0,imm=0) "
|
|
|
|
"R1_w=ptr_foo(id=2,ref_obj_id=2,off=0,imm=0) refs=2\n6: (85) call bpf_this_cpu_ptr#154\n"
|
|
|
|
"R1 type=ptr_ expected=percpu_ptr_" },
|
|
|
|
{ "lock_id_global_zero",
|
bpf: emit map name in register state if applicable and available
In complicated real-world applications, whenever debugging some
verification error through verifier log, it often would be very useful
to see map name for PTR_TO_MAP_VALUE register. Usually this needs to be
inferred from key/value sizes and maybe trying to guess C code location,
but it's not always clear.
Given verifier has the name, and it's never too long, let's just emit it
for ptr_to_map_key, ptr_to_map_value, and const_ptr_to_map registers. We
reshuffle the order a bit, so that map name, key size, and value size
appear before offset and immediate values, which seems like a more
logical order.
Current output:
R1_w=map_ptr(map=array_map,ks=4,vs=8,off=0,imm=0)
But we'll get rid of useless off=0 and imm=0 parts in the next patch.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231118034623.3320920-6-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-11-17 19:46:20 -08:00
|
|
|
"; R1_w=map_value(map=.data.A,ks=4,vs=4,off=0,imm=0)\n2: (85) call bpf_this_cpu_ptr#154\n"
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
"R1 type=map_value expected=percpu_ptr_" },
|
|
|
|
{ "lock_id_mapval_preserve",
|
2023-08-08 19:27:55 +03:00
|
|
|
"[0-9]\\+: (bf) r1 = r0 ;"
|
bpf: emit map name in register state if applicable and available
In complicated real-world applications, whenever debugging some
verification error through verifier log, it often would be very useful
to see map name for PTR_TO_MAP_VALUE register. Usually this needs to be
inferred from key/value sizes and maybe trying to guess C code location,
but it's not always clear.
Given verifier has the name, and it's never too long, let's just emit it
for ptr_to_map_key, ptr_to_map_value, and const_ptr_to_map registers. We
reshuffle the order a bit, so that map name, key size, and value size
appear before offset and immediate values, which seems like a more
logical order.
Current output:
R1_w=map_ptr(map=array_map,ks=4,vs=8,off=0,imm=0)
But we'll get rid of useless off=0 and imm=0 parts in the next patch.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231118034623.3320920-6-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-11-17 19:46:20 -08:00
|
|
|
" R0_w=map_value(id=1,map=array_map,ks=4,vs=8,off=0,imm=0)"
|
|
|
|
" R1_w=map_value(id=1,map=array_map,ks=4,vs=8,off=0,imm=0)\n"
|
2023-08-08 19:27:55 +03:00
|
|
|
"[0-9]\\+: (85) call bpf_this_cpu_ptr#154\n"
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
"R1 type=map_value expected=percpu_ptr_" },
|
|
|
|
{ "lock_id_innermapval_preserve",
|
2023-08-08 19:27:55 +03:00
|
|
|
"[0-9]\\+: (bf) r1 = r0 ;"
|
bpf: emit map name in register state if applicable and available
In complicated real-world applications, whenever debugging some
verification error through verifier log, it often would be very useful
to see map name for PTR_TO_MAP_VALUE register. Usually this needs to be
inferred from key/value sizes and maybe trying to guess C code location,
but it's not always clear.
Given verifier has the name, and it's never too long, let's just emit it
for ptr_to_map_key, ptr_to_map_value, and const_ptr_to_map registers. We
reshuffle the order a bit, so that map name, key size, and value size
appear before offset and immediate values, which seems like a more
logical order.
Current output:
R1_w=map_ptr(map=array_map,ks=4,vs=8,off=0,imm=0)
But we'll get rid of useless off=0 and imm=0 parts in the next patch.
Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Acked-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20231118034623.3320920-6-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2023-11-17 19:46:20 -08:00
|
|
|
" R0=map_value(id=2,ks=4,vs=8,off=0,imm=0)"
|
|
|
|
" R1_w=map_value(id=2,ks=4,vs=8,off=0,imm=0)\n"
|
2023-08-08 19:27:55 +03:00
|
|
|
"[0-9]\\+: (85) call bpf_this_cpu_ptr#154\n"
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
"R1 type=map_value expected=percpu_ptr_" },
|
|
|
|
{ "lock_id_mismatch_kptr_kptr", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_kptr_global", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_kptr_mapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_kptr_innermapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_global_global", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_global_kptr", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_global_mapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_global_innermapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_mapval_mapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_mapval_kptr", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_mapval_global", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_mapval_innermapval", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_innermapval_innermapval1", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_innermapval_innermapval2", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_innermapval_kptr", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_innermapval_global", "bpf_spin_unlock of different lock" },
|
|
|
|
{ "lock_id_mismatch_innermapval_mapval", "bpf_spin_unlock of different lock" },
|
|
|
|
};
|
|
|
|
|
2023-08-08 19:27:55 +03:00
|
|
|
static int match_regex(const char *pattern, const char *string)
|
|
|
|
{
|
|
|
|
int err, rc;
|
|
|
|
regex_t re;
|
|
|
|
|
|
|
|
err = regcomp(&re, pattern, REG_NOSUB);
|
|
|
|
if (err) {
|
|
|
|
char errbuf[512];
|
|
|
|
|
|
|
|
regerror(err, &re, errbuf, sizeof(errbuf));
|
|
|
|
PRINT_FAIL("Can't compile regex: %s\n", errbuf);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
rc = regexec(&re, string, 0, NULL, 0);
|
|
|
|
regfree(&re);
|
|
|
|
return rc == 0 ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
static void test_spin_lock_fail_prog(const char *prog_name, const char *err_msg)
|
|
|
|
{
|
|
|
|
LIBBPF_OPTS(bpf_object_open_opts, opts, .kernel_log_buf = log_buf,
|
|
|
|
.kernel_log_size = sizeof(log_buf),
|
|
|
|
.kernel_log_level = 1);
|
|
|
|
struct test_spin_lock_fail *skel;
|
|
|
|
struct bpf_program *prog;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
skel = test_spin_lock_fail__open_opts(&opts);
|
|
|
|
if (!ASSERT_OK_PTR(skel, "test_spin_lock_fail__open_opts"))
|
|
|
|
return;
|
|
|
|
|
|
|
|
prog = bpf_object__find_program_by_name(skel->obj, prog_name);
|
|
|
|
if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
bpf_program__set_autoload(prog, true);
|
|
|
|
|
|
|
|
ret = test_spin_lock_fail__load(skel);
|
|
|
|
if (!ASSERT_ERR(ret, "test_spin_lock_fail__load must fail"))
|
|
|
|
goto end;
|
|
|
|
|
2022-11-19 00:29:38 +05:30
|
|
|
/* Skip check if JIT does not support kfuncs */
|
|
|
|
if (strstr(log_buf, "JIT does not support calling kernel function")) {
|
|
|
|
test__skip();
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
2023-08-08 19:27:55 +03:00
|
|
|
ret = match_regex(err_msg, log_buf);
|
|
|
|
if (!ASSERT_GE(ret, 0, "match_regex"))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
if (!ASSERT_TRUE(ret, "no match for expected error message")) {
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
fprintf(stderr, "Expected: %s\n", err_msg);
|
|
|
|
fprintf(stderr, "Verifier: %s\n", log_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
end:
|
|
|
|
test_spin_lock_fail__destroy(skel);
|
|
|
|
}
|
2022-11-18 07:26:10 +05:30
|
|
|
|
|
|
|
static void *spin_lock_thread(void *arg)
|
|
|
|
{
|
|
|
|
int err, prog_fd = *(u32 *) arg;
|
|
|
|
LIBBPF_OPTS(bpf_test_run_opts, topts,
|
|
|
|
.data_in = &pkt_v4,
|
|
|
|
.data_size_in = sizeof(pkt_v4),
|
|
|
|
.repeat = 10000,
|
|
|
|
);
|
|
|
|
|
|
|
|
err = bpf_prog_test_run_opts(prog_fd, &topts);
|
|
|
|
ASSERT_OK(err, "test_run");
|
|
|
|
ASSERT_OK(topts.retval, "test_run retval");
|
|
|
|
pthread_exit(arg);
|
|
|
|
}
|
|
|
|
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
void test_spin_lock_success(void)
|
2022-11-18 07:26:10 +05:30
|
|
|
{
|
|
|
|
struct test_spin_lock *skel;
|
|
|
|
pthread_t thread_id[4];
|
|
|
|
int prog_fd, i;
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
skel = test_spin_lock__open_and_load();
|
|
|
|
if (!ASSERT_OK_PTR(skel, "test_spin_lock__open_and_load"))
|
|
|
|
return;
|
|
|
|
prog_fd = bpf_program__fd(skel->progs.bpf_spin_lock_test);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = pthread_create(&thread_id[i], NULL, &spin_lock_thread, &prog_fd);
|
|
|
|
if (!ASSERT_OK(err, "pthread_create"))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
if (!ASSERT_OK(pthread_join(thread_id[i], &ret), "pthread_join"))
|
|
|
|
goto end;
|
|
|
|
if (!ASSERT_EQ(ret, &prog_fd, "ret == prog_fd"))
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
test_spin_lock__destroy(skel);
|
|
|
|
}
|
selftests/bpf: Add failure test cases for spin lock pairing
First, ensure that whenever a bpf_spin_lock is present in an allocation,
the reg->id is preserved. This won't be true for global variables
however, since they have a single map value per map, hence the verifier
harcodes it to 0 (so that multiple pseudo ldimm64 insns can yield the
same lock object per map at a given offset).
Next, add test cases for all possible combinations (kptr, global, map
value, inner map value). Since we lifted restriction on locking in inner
maps, also add test cases for them. Currently, each lookup into an inner
map gets a fresh reg->id, so even if the reg->map_ptr is same, they will
be treated as separate allocations and the incorrect unlock pairing will
be rejected.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20221118015614.2013203-22-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2022-11-18 07:26:11 +05:30
|
|
|
|
|
|
|
void test_spin_lock(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
test_spin_lock_success();
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(spin_lock_fail_tests); i++) {
|
|
|
|
if (!test__start_subtest(spin_lock_fail_tests[i].prog_name))
|
|
|
|
continue;
|
|
|
|
test_spin_lock_fail_prog(spin_lock_fail_tests[i].prog_name,
|
|
|
|
spin_lock_fail_tests[i].err_msg);
|
|
|
|
}
|
|
|
|
}
|