// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2021 Facebook */ #include "vmlinux.h" #include <bpf/bpf_helpers.h> #include "bpf_misc.h" char _license[] SEC("license") = "GPL"; struct callback_ctx { int output; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 32); __type(key, int); __type(value, int); } map1 SEC(".maps"); /* These should be set by the user program */ u32 nested_callback_nr_loops; u32 stop_index = -1; u32 nr_loops; int pid; int callback_selector; /* Making these global variables so that the userspace program * can verify the output through the skeleton */ int nr_loops_returned; int g_output; int err; static int callback(__u32 index, void *data) { struct callback_ctx *ctx = data; if (index >= stop_index) return 1; ctx->output += index; return 0; } static int empty_callback(__u32 index, void *data) { return 0; } static int nested_callback2(__u32 index, void *data) { nr_loops_returned += bpf_loop(nested_callback_nr_loops, callback, data, 0); return 0; } static int nested_callback1(__u32 index, void *data) { bpf_loop(nested_callback_nr_loops, nested_callback2, data, 0); return 0; } SEC("fentry/" SYS_PREFIX "sys_nanosleep") int test_prog(void *ctx) { struct callback_ctx data = {}; if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; nr_loops_returned = bpf_loop(nr_loops, callback, &data, 0); if (nr_loops_returned < 0) err = nr_loops_returned; else g_output = data.output; return 0; } SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_null_ctx(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; nr_loops_returned = bpf_loop(nr_loops, empty_callback, NULL, 0); return 0; } SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_invalid_flags(void *ctx) { struct callback_ctx data = {}; if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; err = bpf_loop(nr_loops, callback, &data, 1); return 0; } SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_nested_calls(void *ctx) { struct callback_ctx data = {}; if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; nr_loops_returned = 0; bpf_loop(nr_loops, nested_callback1, &data, 0); g_output = data.output; return 0; } static int callback_set_f0(int i, void *ctx) { g_output = 0xF0; return 0; } static int callback_set_0f(int i, void *ctx) { g_output = 0x0F; return 0; } /* * non-constant callback is a corner case for bpf_loop inline logic */ SEC("fentry/" SYS_PREFIX "sys_nanosleep") int prog_non_constant_callback(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; int (*callback)(int i, void *ctx); g_output = 0; if (callback_selector == 0x0F) callback = callback_set_0f; else callback = callback_set_f0; bpf_loop(1, callback, NULL, 0); return 0; } static int stack_check_inner_callback(void *ctx) { return 0; } static int map1_lookup_elem(int key) { int *val = bpf_map_lookup_elem(&map1, &key); return val ? *val : -1; } static void map1_update_elem(int key, int val) { bpf_map_update_elem(&map1, &key, &val, BPF_ANY); } static int stack_check_outer_callback(void *ctx) { int a = map1_lookup_elem(1); int b = map1_lookup_elem(2); int c = map1_lookup_elem(3); int d = map1_lookup_elem(4); int e = map1_lookup_elem(5); int f = map1_lookup_elem(6); bpf_loop(1, stack_check_inner_callback, NULL, 0); map1_update_elem(1, a + 1); map1_update_elem(2, b + 1); map1_update_elem(3, c + 1); map1_update_elem(4, d + 1); map1_update_elem(5, e + 1); map1_update_elem(6, f + 1); return 0; } /* Some of the local variables in stack_check and * stack_check_outer_callback would be allocated on stack by * compiler. This test should verify that stack content for these * variables is preserved between calls to bpf_loop (might be an issue * if loop inlining allocates stack slots incorrectly). */ SEC("fentry/" SYS_PREFIX "sys_nanosleep") int stack_check(void *ctx) { if (bpf_get_current_pid_tgid() >> 32 != pid) return 0; int a = map1_lookup_elem(7); int b = map1_lookup_elem(8); int c = map1_lookup_elem(9); int d = map1_lookup_elem(10); int e = map1_lookup_elem(11); int f = map1_lookup_elem(12); bpf_loop(1, stack_check_outer_callback, NULL, 0); map1_update_elem(7, a + 1); map1_update_elem(8, b + 1); map1_update_elem(9, c + 1); map1_update_elem(10, d + 1); map1_update_elem(11, e + 1); map1_update_elem(12, f + 1); return 0; }