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",
|
|
|
|
"; R1_w=map_value(off=0,ks=4,vs=4,imm=0)\n2: (85) call bpf_this_cpu_ptr#154\n"
|
|
|
|
"R1 type=map_value expected=percpu_ptr_" },
|
|
|
|
{ "lock_id_mapval_preserve",
|
2023-08-08 19:27:55 +03:00
|
|
|
"[0-9]\\+: (bf) r1 = r0 ;"
|
|
|
|
" R0_w=map_value(id=1,off=0,ks=4,vs=8,imm=0)"
|
|
|
|
" R1_w=map_value(id=1,off=0,ks=4,vs=8,imm=0)\n"
|
|
|
|
"[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 ;"
|
|
|
|
" R0=map_value(id=2,off=0,ks=4,vs=8,imm=0)"
|
|
|
|
" R1_w=map_value(id=2,off=0,ks=4,vs=8,imm=0)\n"
|
|
|
|
"[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);
|
|
|
|
}
|
|
|
|
}
|