Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Andrii Nakryiko 1354 100.00% 5 100.00%
Total 1354 5


// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bpf_misc.h"
#include "xdp_metadata.h"
#include "bpf_kfuncs.h"

int arr[1];
int unkn_idx;
const volatile bool call_dead_subprog = false;

__noinline long global_bad(void)
{
	return arr[unkn_idx]; /* BOOM */
}

__noinline long global_good(void)
{
	return arr[0];
}

__noinline long global_calls_bad(void)
{
	return global_good() + global_bad() /* does BOOM indirectly */;
}

__noinline long global_calls_good_only(void)
{
	return global_good();
}

__noinline long global_dead(void)
{
	return arr[0] * 2;
}

SEC("?raw_tp")
__success __log_level(2)
/* main prog is validated completely first */
__msg("('global_calls_good_only') is global and assumed valid.")
/* eventually global_good() is transitively validated as well */
__msg("Validating global_good() func")
__msg("('global_good') is safe for any args that match its prototype")
int chained_global_func_calls_success(void)
{
	int sum = 0;

	if (call_dead_subprog)
		sum += global_dead();
	return global_calls_good_only() + sum;
}

SEC("?raw_tp")
__failure __log_level(2)
/* main prog validated successfully first */
__msg("('global_calls_bad') is global and assumed valid.")
/* eventually we validate global_bad() and fail */
__msg("Validating global_bad() func")
__msg("math between map_value pointer and register") /* BOOM */
int chained_global_func_calls_bad(void)
{
	return global_calls_bad();
}

/* do out of bounds access forcing verifier to fail verification if this
 * global func is called
 */
__noinline int global_unsupp(const int *mem)
{
	if (!mem)
		return 0;
	return mem[100]; /* BOOM */
}

const volatile bool skip_unsupp_global = true;

SEC("?raw_tp")
__success
int guarded_unsupp_global_called(void)
{
	if (!skip_unsupp_global)
		return global_unsupp(NULL);
	return 0;
}

SEC("?raw_tp")
__failure __log_level(2)
__msg("Func#1 ('global_unsupp') is global and assumed valid.")
__msg("Validating global_unsupp() func#1...")
__msg("value is outside of the allowed memory range")
int unguarded_unsupp_global_called(void)
{
	int x = 0;

	return global_unsupp(&x);
}

long stack[128];

__weak int subprog_nullable_ptr_bad(int *p)
{
	return (*p) * 2; /* bad, missing null check */
}

SEC("?raw_tp")
__failure __log_level(2)
__msg("invalid mem access 'mem_or_null'")
int arg_tag_nullable_ptr_fail(void *ctx)
{
	int x = 42;

	return subprog_nullable_ptr_bad(&x);
}

typedef struct {
	int x;
} user_struct_t;

__noinline __weak int subprog_user_anon_mem(user_struct_t *t)
{
	return t ? t->x : 0;
}

SEC("?tracepoint")
__failure __log_level(2)
__msg("invalid bpf_context access")
__msg("Caller passes invalid args into func#1 ('subprog_user_anon_mem')")
int anon_user_mem_invalid(void *ctx)
{
	/* can't pass PTR_TO_CTX as user memory */
	return subprog_user_anon_mem(ctx);
}

SEC("?tracepoint")
__success __log_level(2)
__msg("Func#1 ('subprog_user_anon_mem') is safe for any args that match its prototype")
int anon_user_mem_valid(void *ctx)
{
	user_struct_t t = { .x = 42 };

	return subprog_user_anon_mem(&t);
}

__noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __arg_nonnull)
{
	return (*p1) * (*p2); /* good, no need for NULL checks */
}

int x = 47;

SEC("?raw_tp")
__success __log_level(2)
int arg_tag_nonnull_ptr_good(void *ctx)
{
	int y = 74;

	return subprog_nonnull_ptr_good(&x, &y);
}

/* this global subprog can be now called from many types of entry progs, each
 * with different context type
 */
__weak int subprog_ctx_tag(void *ctx __arg_ctx)
{
	return bpf_get_stack(ctx, stack, sizeof(stack), 0);
}

__weak int raw_tp_canonical(struct bpf_raw_tracepoint_args *ctx __arg_ctx)
{
	return 0;
}

__weak int raw_tp_u64_array(u64 *ctx __arg_ctx)
{
	return 0;
}

SEC("?raw_tp")
__success __log_level(2)
int arg_tag_ctx_raw_tp(void *ctx)
{
	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
}

SEC("?raw_tp.w")
__success __log_level(2)
int arg_tag_ctx_raw_tp_writable(void *ctx)
{
	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
}

SEC("?tp_btf/sys_enter")
__success __log_level(2)
int arg_tag_ctx_raw_tp_btf(void *ctx)
{
	return subprog_ctx_tag(ctx) + raw_tp_canonical(ctx) + raw_tp_u64_array(ctx);
}

struct whatever { };

__weak int tp_whatever(struct whatever *ctx __arg_ctx)
{
	return 0;
}

SEC("?tp")
__success __log_level(2)
int arg_tag_ctx_tp(void *ctx)
{
	return subprog_ctx_tag(ctx) + tp_whatever(ctx);
}

__weak int kprobe_subprog_pt_regs(struct pt_regs *ctx __arg_ctx)
{
	return 0;
}

__weak int kprobe_subprog_typedef(bpf_user_pt_regs_t *ctx __arg_ctx)
{
	return 0;
}

SEC("?kprobe")
__success __log_level(2)
int arg_tag_ctx_kprobe(void *ctx)
{
	return subprog_ctx_tag(ctx) +
	       kprobe_subprog_pt_regs(ctx) +
	       kprobe_subprog_typedef(ctx);
}

__weak int perf_subprog_regs(
#if defined(bpf_target_riscv)
	struct user_regs_struct *ctx __arg_ctx
#elif defined(bpf_target_s390)
	/* user_pt_regs typedef is anonymous struct, so only `void *` works */
	void *ctx __arg_ctx
#elif defined(bpf_target_loongarch) || defined(bpf_target_arm64) || defined(bpf_target_powerpc)
	struct user_pt_regs *ctx __arg_ctx
#else
	struct pt_regs *ctx __arg_ctx
#endif
)
{
	return 0;
}

__weak int perf_subprog_typedef(bpf_user_pt_regs_t *ctx __arg_ctx)
{
	return 0;
}

__weak int perf_subprog_canonical(struct bpf_perf_event_data *ctx __arg_ctx)
{
	return 0;
}

SEC("?perf_event")
__success __log_level(2)
int arg_tag_ctx_perf(void *ctx)
{
	return subprog_ctx_tag(ctx) +
	       perf_subprog_regs(ctx) +
	       perf_subprog_typedef(ctx) +
	       perf_subprog_canonical(ctx);
}

__weak int iter_subprog_void(void *ctx __arg_ctx)
{
	return 0;
}

__weak int iter_subprog_typed(struct bpf_iter__task *ctx __arg_ctx)
{
	return 0;
}

SEC("?iter/task")
__success __log_level(2)
int arg_tag_ctx_iter_task(struct bpf_iter__task *ctx)
{
	return (iter_subprog_void(ctx) + iter_subprog_typed(ctx)) & 1;
}

__weak int tracing_subprog_void(void *ctx __arg_ctx)
{
	return 0;
}

__weak int tracing_subprog_u64(u64 *ctx __arg_ctx)
{
	return 0;
}

int acc;

SEC("?fentry/" SYS_PREFIX "sys_nanosleep")
__success __log_level(2)
int BPF_PROG(arg_tag_ctx_fentry)
{
	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
	return 0;
}

SEC("?fexit/" SYS_PREFIX "sys_nanosleep")
__success __log_level(2)
int BPF_PROG(arg_tag_ctx_fexit)
{
	acc += tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
	return 0;
}

SEC("?fmod_ret/" SYS_PREFIX "sys_nanosleep")
__success __log_level(2)
int BPF_PROG(arg_tag_ctx_fmod_ret)
{
	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
}

SEC("?lsm/bpf")
__success __log_level(2)
int BPF_PROG(arg_tag_ctx_lsm)
{
	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
}

SEC("?struct_ops/test_1")
__success __log_level(2)
int BPF_PROG(arg_tag_ctx_struct_ops)
{
	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx);
}

SEC(".struct_ops")
struct bpf_dummy_ops dummy_1 = {
	.test_1 = (void *)arg_tag_ctx_struct_ops,
};

SEC("?syscall")
__success __log_level(2)
int arg_tag_ctx_syscall(void *ctx)
{
	return tracing_subprog_void(ctx) + tracing_subprog_u64(ctx) + tp_whatever(ctx);
}

__weak int subprog_dynptr(struct bpf_dynptr *dptr)
{
	long *d, t, buf[1] = {};

	d = bpf_dynptr_data(dptr, 0, sizeof(long));
	if (!d)
		return 0;

	t = *d + 1;

	d = bpf_dynptr_slice(dptr, 0, &buf, sizeof(long));
	if (!d)
		return t;

	t = *d + 2;

	return t;
}

SEC("?xdp")
__success __log_level(2)
int arg_tag_dynptr(struct xdp_md *ctx)
{
	struct bpf_dynptr dptr;

	bpf_dynptr_from_xdp(ctx, 0, &dptr);

	return subprog_dynptr(&dptr);
}

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