Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions kernel/bpf/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2419,8 +2419,7 @@ static bool __bpf_prog_map_compatible(struct bpf_map *map,
break;
cookie = aux->cgroup_storage[i] ?
aux->cgroup_storage[i]->cookie : 0;
ret = map->owner->storage_cookie[i] == cookie ||
!cookie;
ret = map->owner->storage_cookie[i] == cookie;
}
if (ret &&
map->owner->attach_func_proto != aux->attach_func_proto) {
Expand Down
119 changes: 119 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/tailcalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include "tailcall_freplace.skel.h"
#include "tc_bpf2bpf.skel.h"
#include "tailcall_fail.skel.h"
#include "tailcall_cgrp_storage_owner.skel.h"
#include "tailcall_cgrp_storage_no_storage.skel.h"
#include "tailcall_cgrp_storage.skel.h"

/* test_tailcall_1 checks basic functionality by patching multiple locations
* in a single program for a single tail call slot with nop->jmp, jmp->nop
Expand Down Expand Up @@ -1648,6 +1651,116 @@ static void test_tailcall_bpf2bpf_freplace(void)
tc_bpf2bpf__destroy(tc_skel);
}

/*
* test_tail_call_cgrp_storage checks that if the owner program of a program
* array uses cgroup storage, other callers and callees must also use the
* exact same cgroup storage.
*/
static void test_tailcall_cgrp_storage(void)
{
int err, prog_fd, prog_array_fd, storage_map_fd, key = 0;
struct tailcall_cgrp_storage_owner *owner_skel;
struct tailcall_cgrp_storage *skel;

/* Load owner_skel first to make sure it becomes the owner of prog_array */
owner_skel = tailcall_cgrp_storage_owner__open_and_load();
if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load"))
return;

prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
storage_map_fd = bpf_map__fd(owner_skel->maps.storage_map);

skel = tailcall_cgrp_storage__open();
if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open")) {
tailcall_cgrp_storage_owner__destroy(owner_skel);
return;
}

err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)");

err = bpf_map__reuse_fd(skel->maps.storage_map, storage_map_fd);
ASSERT_OK(err, "bpf_map__reuse_fd(storage_map)");

err = bpf_object__load(skel->obj);
ASSERT_OK(err, "bpf_object__load");

prog_fd = bpf_program__fd(skel->progs.callee_prog);

err = bpf_map_update_elem(prog_array_fd, &key, &prog_fd, BPF_ANY);
ASSERT_OK(err, "bpf_map_update_elem(prog_array)");

tailcall_cgrp_storage__destroy(skel);
tailcall_cgrp_storage_owner__destroy(owner_skel);
}

/*
* test_tail_call_cgrp_storage_diff_storage checks that a program using tail call
* is rejected if it uses a cgroup storage different from the owner's.
*/
static void test_tailcall_cgrp_storage_diff_storage(void)
{
struct tailcall_cgrp_storage_owner *owner_skel;
struct tailcall_cgrp_storage *skel;
int err, prog_array_fd;

/* Load owner_skel first to make sure it becomes the owner of prog_array */
owner_skel = tailcall_cgrp_storage_owner__open_and_load();
if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load"))
return;

prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);

skel = tailcall_cgrp_storage__open();
if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage__open")) {
tailcall_cgrp_storage_owner__destroy(owner_skel);
return;
}

err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)");

err = bpf_object__load(skel->obj);
ASSERT_ERR(err, "bpf_object__load");

tailcall_cgrp_storage__destroy(skel);
tailcall_cgrp_storage_owner__destroy(owner_skel);
}

/*
* test_tail_call_cgrp_storage_no_storage checks that a program using tail call
* is rejected if it does not use cgroup storage while the owner does.
*/
static void test_tailcall_cgrp_storage_no_storage(void)
{
struct tailcall_cgrp_storage_owner *owner_skel;
struct tailcall_cgrp_storage_no_storage *skel;
int err, prog_array_fd, storage_map_fd;

/* Load owner_skel first to make sure it becomes the owner of prog_array */
owner_skel = tailcall_cgrp_storage_owner__open_and_load();
if (!ASSERT_OK_PTR(owner_skel, "tailcall_cgrp_storage_owner__open_and_load"))
return;

prog_array_fd = bpf_map__fd(owner_skel->maps.prog_array);
storage_map_fd = bpf_map__fd(owner_skel->maps.storage_map);

skel = tailcall_cgrp_storage_no_storage__open();
if (!ASSERT_OK_PTR(skel, "tailcall_cgrp_storage_no_storage__open")) {
tailcall_cgrp_storage_owner__destroy(owner_skel);
return;
}

err = bpf_map__reuse_fd(skel->maps.prog_array, prog_array_fd);
ASSERT_OK(err, "bpf_map__reuse_fd(prog_array)");

err = bpf_object__load(skel->obj);
ASSERT_ERR(err, "bpf_object__load");

tailcall_cgrp_storage_no_storage__destroy(skel);
tailcall_cgrp_storage_owner__destroy(owner_skel);
}

static void test_tailcall_failure()
{
RUN_TESTS(tailcall_fail);
Expand Down Expand Up @@ -1705,6 +1818,12 @@ void test_tailcalls(void)
test_tailcall_freplace();
if (test__start_subtest("tailcall_bpf2bpf_freplace"))
test_tailcall_bpf2bpf_freplace();
if (test__start_subtest("tailcall_cgrp_storage"))
test_tailcall_cgrp_storage();
if (test__start_subtest("tailcall_cgrp_storage_diff_storage"))
test_tailcall_cgrp_storage_diff_storage();
if (test__start_subtest("tailcall_cgrp_storage_no_storage"))
test_tailcall_cgrp_storage_no_storage();
if (test__start_subtest("tailcall_failure"))
test_tailcall_failure();
}
45 changes: 45 additions & 0 deletions tools/testing/selftests/bpf/progs/tailcall_cgrp_storage.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: GPL-2.0

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, __u64);
} storage_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} prog_array SEC(".maps");

SEC("cgroup_skb/egress")
int caller_prog(struct __sk_buff *skb)
{
__u64 *storage;

storage = bpf_get_local_storage(&storage_map, 0);
if (storage)
*storage = 1;

bpf_tail_call(skb, &prog_array, 0);

return 1;
}

SEC("cgroup_skb/egress")
int callee_prog(struct __sk_buff *skb)
{
__u64 *storage;

storage = bpf_get_local_storage(&storage_map, 0);
if (storage)
*storage = 1;

return 1;
}

char _license[] SEC("license") = "GPL";
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} prog_array SEC(".maps");

SEC("cgroup_skb/egress")
int caller_prog(struct __sk_buff *skb)
{
bpf_tail_call(skb, &prog_array, 0);

return 1;
}

char _license[] SEC("license") = "GPL";
33 changes: 33 additions & 0 deletions tools/testing/selftests/bpf/progs/tailcall_cgrp_storage_owner.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
__type(key, struct bpf_cgroup_storage_key);
__type(value, __u64);
} storage_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} prog_array SEC(".maps");

SEC("cgroup_skb/egress")
int prog_array_owner(struct __sk_buff *skb)
{
__u64 *storage;

storage = bpf_get_local_storage(&storage_map, 0);
if (storage)
*storage = 1;

bpf_tail_call(skb, &prog_array, 0);

return 1;
}

char _license[] SEC("license") = "GPL";
Loading