// 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;
}