mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-12 00:34:43 +08:00
8be6327959
Test verifier/ref_tracking automatically converted to use inline assembly. Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20230421174234.2391278-16-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1496 lines
37 KiB
C
1496 lines
37 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Converted from tools/testing/selftests/bpf/verifier/ref_tracking.c */
|
|
|
|
#include <linux/bpf.h>
|
|
#include <bpf/bpf_helpers.h>
|
|
#include "../../../include/linux/filter.h"
|
|
#include "bpf_misc.h"
|
|
|
|
#define BPF_SK_LOOKUP(func) \
|
|
/* struct bpf_sock_tuple tuple = {} */ \
|
|
"r2 = 0;" \
|
|
"*(u32*)(r10 - 8) = r2;" \
|
|
"*(u64*)(r10 - 16) = r2;" \
|
|
"*(u64*)(r10 - 24) = r2;" \
|
|
"*(u64*)(r10 - 32) = r2;" \
|
|
"*(u64*)(r10 - 40) = r2;" \
|
|
"*(u64*)(r10 - 48) = r2;" \
|
|
/* sk = func(ctx, &tuple, sizeof tuple, 0, 0) */ \
|
|
"r2 = r10;" \
|
|
"r2 += -48;" \
|
|
"r3 = %[sizeof_bpf_sock_tuple];"\
|
|
"r4 = 0;" \
|
|
"r5 = 0;" \
|
|
"call %[" #func "];"
|
|
|
|
struct bpf_key {} __attribute__((preserve_access_index));
|
|
|
|
extern void bpf_key_put(struct bpf_key *key) __ksym;
|
|
extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;
|
|
extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym;
|
|
|
|
/* BTF FUNC records are not generated for kfuncs referenced
|
|
* from inline assembly. These records are necessary for
|
|
* libbpf to link the program. The function below is a hack
|
|
* to ensure that BTF FUNC records are generated.
|
|
*/
|
|
void __kfunc_btf_root(void)
|
|
{
|
|
bpf_key_put(0);
|
|
bpf_lookup_system_key(0);
|
|
bpf_lookup_user_key(0, 0);
|
|
}
|
|
|
|
#define MAX_ENTRIES 11
|
|
|
|
struct test_val {
|
|
unsigned int index;
|
|
int foo[MAX_ENTRIES];
|
|
};
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
|
__uint(max_entries, 1);
|
|
__type(key, int);
|
|
__type(value, struct test_val);
|
|
} map_array_48b SEC(".maps");
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
|
__uint(max_entries, 4096);
|
|
} map_ringbuf SEC(".maps");
|
|
|
|
void dummy_prog_42_tc(void);
|
|
void dummy_prog_24_tc(void);
|
|
void dummy_prog_loop1_tc(void);
|
|
|
|
struct {
|
|
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
|
__uint(max_entries, 4);
|
|
__uint(key_size, sizeof(int));
|
|
__array(values, void (void));
|
|
} map_prog1_tc SEC(".maps") = {
|
|
.values = {
|
|
[0] = (void *)&dummy_prog_42_tc,
|
|
[1] = (void *)&dummy_prog_loop1_tc,
|
|
[2] = (void *)&dummy_prog_24_tc,
|
|
},
|
|
};
|
|
|
|
SEC("tc")
|
|
__auxiliary
|
|
__naked void dummy_prog_42_tc(void)
|
|
{
|
|
asm volatile ("r0 = 42; exit;");
|
|
}
|
|
|
|
SEC("tc")
|
|
__auxiliary
|
|
__naked void dummy_prog_24_tc(void)
|
|
{
|
|
asm volatile ("r0 = 24; exit;");
|
|
}
|
|
|
|
SEC("tc")
|
|
__auxiliary
|
|
__naked void dummy_prog_loop1_tc(void)
|
|
{
|
|
asm volatile (" \
|
|
r3 = 1; \
|
|
r2 = %[map_prog1_tc] ll; \
|
|
call %[bpf_tail_call]; \
|
|
r0 = 41; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_tail_call),
|
|
__imm_addr(map_prog1_tc)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak potential reference")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void reference_tracking_leak_potential_reference(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; /* leak reference */ \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak potential reference to sock_common")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void potential_reference_to_sock_common_1(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_skc_lookup_tcp)
|
|
" r6 = r0; /* leak reference */ \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_skc_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak potential reference on stack")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void leak_potential_reference_on_stack(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r4 = r10; \
|
|
r4 += -8; \
|
|
*(u64*)(r4 + 0) = r0; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak potential reference on stack 2")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void potential_reference_on_stack_2(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r4 = r10; \
|
|
r4 += -8; \
|
|
*(u64*)(r4 + 0) = r0; \
|
|
r0 = 0; \
|
|
r1 = 0; \
|
|
*(u64*)(r4 + 0) = r1; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: zero potential reference")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void reference_tracking_zero_potential_reference(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r0 = 0; /* leak reference */ \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: zero potential reference to sock_common")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void potential_reference_to_sock_common_2(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_skc_lookup_tcp)
|
|
" r0 = 0; /* leak reference */ \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_skc_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: copy and zero potential references")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void copy_and_zero_potential_references(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r7 = r0; \
|
|
r0 = 0; \
|
|
r7 = 0; /* leak reference */ \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: acquire/release user key reference")
|
|
__success
|
|
__naked void acquire_release_user_key_reference(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = -3; \
|
|
r2 = 0; \
|
|
call %[bpf_lookup_user_key]; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r1 = r0; \
|
|
call %[bpf_key_put]; \
|
|
l0_%=: r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_key_put),
|
|
__imm(bpf_lookup_user_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: acquire/release system key reference")
|
|
__success
|
|
__naked void acquire_release_system_key_reference(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = 1; \
|
|
call %[bpf_lookup_system_key]; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r1 = r0; \
|
|
call %[bpf_key_put]; \
|
|
l0_%=: r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_key_put),
|
|
__imm(bpf_lookup_system_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: release user key reference without check")
|
|
__failure __msg("Possibly NULL pointer passed to trusted arg0")
|
|
__naked void user_key_reference_without_check(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = -3; \
|
|
r2 = 0; \
|
|
call %[bpf_lookup_user_key]; \
|
|
r1 = r0; \
|
|
call %[bpf_key_put]; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_key_put),
|
|
__imm(bpf_lookup_user_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: release system key reference without check")
|
|
__failure __msg("Possibly NULL pointer passed to trusted arg0")
|
|
__naked void system_key_reference_without_check(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = 1; \
|
|
call %[bpf_lookup_system_key]; \
|
|
r1 = r0; \
|
|
call %[bpf_key_put]; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_key_put),
|
|
__imm(bpf_lookup_system_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: release with NULL key pointer")
|
|
__failure __msg("Possibly NULL pointer passed to trusted arg0")
|
|
__naked void release_with_null_key_pointer(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = 0; \
|
|
call %[bpf_key_put]; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_key_put)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: leak potential reference to user key")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void potential_reference_to_user_key(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = -3; \
|
|
r2 = 0; \
|
|
call %[bpf_lookup_user_key]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_lookup_user_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("lsm.s/bpf")
|
|
__description("reference tracking: leak potential reference to system key")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void potential_reference_to_system_key(void)
|
|
{
|
|
asm volatile (" \
|
|
r1 = 1; \
|
|
call %[bpf_lookup_system_key]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_lookup_system_key)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference without check")
|
|
__failure __msg("type=sock_or_null expected=sock")
|
|
__naked void tracking_release_reference_without_check(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" /* reference in r0 may be NULL */ \
|
|
r1 = r0; \
|
|
r2 = 0; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference to sock_common without check")
|
|
__failure __msg("type=sock_common_or_null expected=sock")
|
|
__naked void to_sock_common_without_check(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_skc_lookup_tcp)
|
|
" /* reference in r0 may be NULL */ \
|
|
r1 = r0; \
|
|
r2 = 0; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_release),
|
|
__imm(bpf_skc_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference")
|
|
__success __retval(0)
|
|
__naked void reference_tracking_release_reference(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference to sock_common")
|
|
__success __retval(0)
|
|
__naked void release_reference_to_sock_common(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_skc_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_release),
|
|
__imm(bpf_skc_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference 2")
|
|
__success __retval(0)
|
|
__naked void reference_tracking_release_reference_2(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference twice")
|
|
__failure __msg("type=scalar expected=sock")
|
|
__naked void reference_tracking_release_reference_twice(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
r6 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference twice inside branch")
|
|
__failure __msg("type=scalar expected=sock")
|
|
__naked void release_reference_twice_inside_branch(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
r6 = r0; \
|
|
if r0 == 0 goto l0_%=; /* goto end */ \
|
|
call %[bpf_sk_release]; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: alloc, check, free in one subbranch")
|
|
__failure __msg("Unreleased reference")
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void check_free_in_one_subbranch(void)
|
|
{
|
|
asm volatile (" \
|
|
r2 = *(u32*)(r1 + %[__sk_buff_data]); \
|
|
r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
|
|
r0 = r2; \
|
|
r0 += 16; \
|
|
/* if (offsetof(skb, mark) > data_len) exit; */ \
|
|
if r0 <= r3 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = *(u32*)(r2 + %[__sk_buff_mark]); \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r6 == 0 goto l1_%=; /* mark == 0? */\
|
|
/* Leak reference in R0 */ \
|
|
exit; \
|
|
l1_%=: if r0 == 0 goto l2_%=; /* sk NULL? */ \
|
|
r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
l2_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
|
|
__imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: alloc, check, free in both subbranches")
|
|
__success __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void check_free_in_both_subbranches(void)
|
|
{
|
|
asm volatile (" \
|
|
r2 = *(u32*)(r1 + %[__sk_buff_data]); \
|
|
r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
|
|
r0 = r2; \
|
|
r0 += 16; \
|
|
/* if (offsetof(skb, mark) > data_len) exit; */ \
|
|
if r0 <= r3 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = *(u32*)(r2 + %[__sk_buff_mark]); \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r6 == 0 goto l1_%=; /* mark == 0? */\
|
|
if r0 == 0 goto l2_%=; /* sk NULL? */ \
|
|
r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
l2_%=: exit; \
|
|
l1_%=: if r0 == 0 goto l3_%=; /* sk NULL? */ \
|
|
r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
l3_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
|
|
__imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: free reference in subprog")
|
|
__success __retval(0)
|
|
__naked void call_free_reference_in_subprog(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; /* unchecked reference */ \
|
|
call call_free_reference_in_subprog__1; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void call_free_reference_in_subprog__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
r2 = r1; \
|
|
if r2 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_release)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: free reference in subprog and outside")
|
|
__failure __msg("type=scalar expected=sock")
|
|
__naked void reference_in_subprog_and_outside(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; /* unchecked reference */ \
|
|
r6 = r0; \
|
|
call reference_in_subprog_and_outside__1; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void reference_in_subprog_and_outside__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
r2 = r1; \
|
|
if r2 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_release)
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: alloc & leak reference in subprog")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void alloc_leak_reference_in_subprog(void)
|
|
{
|
|
asm volatile (" \
|
|
r4 = r10; \
|
|
r4 += -8; \
|
|
call alloc_leak_reference_in_subprog__1; \
|
|
r1 = r0; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void alloc_leak_reference_in_subprog__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
r6 = r4; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" /* spill unchecked sk_ptr into stack of caller */\
|
|
*(u64*)(r6 + 0) = r0; \
|
|
r1 = r0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: alloc in subprog, release outside")
|
|
__success __retval(POINTER_VALUE)
|
|
__naked void alloc_in_subprog_release_outside(void)
|
|
{
|
|
asm volatile (" \
|
|
r4 = r10; \
|
|
call alloc_in_subprog_release_outside__1; \
|
|
r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_release)
|
|
: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void alloc_in_subprog_release_outside__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" exit; /* return sk */ \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: sk_ptr leak into caller stack")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void ptr_leak_into_caller_stack(void)
|
|
{
|
|
asm volatile (" \
|
|
r4 = r10; \
|
|
r4 += -8; \
|
|
call ptr_leak_into_caller_stack__1; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void ptr_leak_into_caller_stack__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
r5 = r10; \
|
|
r5 += -8; \
|
|
*(u64*)(r5 + 0) = r4; \
|
|
call ptr_leak_into_caller_stack__2; \
|
|
/* spill unchecked sk_ptr into stack of caller */\
|
|
r5 = r10; \
|
|
r5 += -8; \
|
|
r4 = *(u64*)(r5 + 0); \
|
|
*(u64*)(r4 + 0) = r0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void ptr_leak_into_caller_stack__2(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 2 */ \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking in call: sk_ptr spill into caller stack")
|
|
__success __retval(0)
|
|
__naked void ptr_spill_into_caller_stack(void)
|
|
{
|
|
asm volatile (" \
|
|
r4 = r10; \
|
|
r4 += -8; \
|
|
call ptr_spill_into_caller_stack__1; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" ::: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void ptr_spill_into_caller_stack__1(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 1 */ \
|
|
r5 = r10; \
|
|
r5 += -8; \
|
|
*(u64*)(r5 + 0) = r4; \
|
|
call ptr_spill_into_caller_stack__2; \
|
|
/* spill unchecked sk_ptr into stack of caller */\
|
|
r5 = r10; \
|
|
r5 += -8; \
|
|
r4 = *(u64*)(r5 + 0); \
|
|
*(u64*)(r4 + 0) = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
/* now the sk_ptr is verified, free the reference */\
|
|
r1 = *(u64*)(r4 + 0); \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_release)
|
|
: __clobber_all);
|
|
}
|
|
|
|
static __naked __noinline __attribute__((used))
|
|
void ptr_spill_into_caller_stack__2(void)
|
|
{
|
|
asm volatile (" \
|
|
/* subprog 2 */ \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: allow LD_ABS")
|
|
__success __retval(0)
|
|
__naked void reference_tracking_allow_ld_abs(void)
|
|
{
|
|
asm volatile (" \
|
|
r6 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: r0 = *(u8*)skb[0]; \
|
|
r0 = *(u16*)skb[0]; \
|
|
r0 = *(u32*)skb[0]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: forbid LD_ABS while holding reference")
|
|
__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references")
|
|
__naked void ld_abs_while_holding_reference(void)
|
|
{
|
|
asm volatile (" \
|
|
r6 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r0 = *(u8*)skb[0]; \
|
|
r0 = *(u16*)skb[0]; \
|
|
r0 = *(u32*)skb[0]; \
|
|
r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: allow LD_IND")
|
|
__success __retval(1)
|
|
__naked void reference_tracking_allow_ld_ind(void)
|
|
{
|
|
asm volatile (" \
|
|
r6 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: r7 = 1; \
|
|
.8byte %[ld_ind]; \
|
|
r0 = r7; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)),
|
|
__imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, -0x200000))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: forbid LD_IND while holding reference")
|
|
__failure __msg("BPF_LD_[ABS|IND] cannot be mixed with socket references")
|
|
__naked void ld_ind_while_holding_reference(void)
|
|
{
|
|
asm volatile (" \
|
|
r6 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r4 = r0; \
|
|
r7 = 1; \
|
|
.8byte %[ld_ind]; \
|
|
r0 = r7; \
|
|
r1 = r4; \
|
|
if r1 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple)),
|
|
__imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, -0x200000))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: check reference or tail call")
|
|
__success __retval(0)
|
|
__naked void check_reference_or_tail_call(void)
|
|
{
|
|
asm volatile (" \
|
|
r7 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" /* if (sk) bpf_sk_release() */ \
|
|
r1 = r0; \
|
|
if r1 != 0 goto l0_%=; \
|
|
/* bpf_tail_call() */ \
|
|
r3 = 3; \
|
|
r2 = %[map_prog1_tc] ll; \
|
|
r1 = r7; \
|
|
call %[bpf_tail_call]; \
|
|
r0 = 0; \
|
|
exit; \
|
|
l0_%=: call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tail_call),
|
|
__imm_addr(map_prog1_tc),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: release reference then tail call")
|
|
__success __retval(0)
|
|
__naked void release_reference_then_tail_call(void)
|
|
{
|
|
asm volatile (" \
|
|
r7 = r1; \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" /* if (sk) bpf_sk_release() */ \
|
|
r1 = r0; \
|
|
if r1 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: /* bpf_tail_call() */ \
|
|
r3 = 3; \
|
|
r2 = %[map_prog1_tc] ll; \
|
|
r1 = r7; \
|
|
call %[bpf_tail_call]; \
|
|
r0 = 0; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tail_call),
|
|
__imm_addr(map_prog1_tc),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak possible reference over tail call")
|
|
__failure __msg("tail_call would lead to reference leak")
|
|
__naked void possible_reference_over_tail_call(void)
|
|
{
|
|
asm volatile (" \
|
|
r7 = r1; \
|
|
/* Look up socket and store in REG_6 */ \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" /* bpf_tail_call() */ \
|
|
r6 = r0; \
|
|
r3 = 3; \
|
|
r2 = %[map_prog1_tc] ll; \
|
|
r1 = r7; \
|
|
call %[bpf_tail_call]; \
|
|
r0 = 0; \
|
|
/* if (sk) bpf_sk_release() */ \
|
|
r1 = r6; \
|
|
if r1 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tail_call),
|
|
__imm_addr(map_prog1_tc),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: leak checked reference over tail call")
|
|
__failure __msg("tail_call would lead to reference leak")
|
|
__naked void checked_reference_over_tail_call(void)
|
|
{
|
|
asm volatile (" \
|
|
r7 = r1; \
|
|
/* Look up socket and store in REG_6 */ \
|
|
" BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
/* if (!sk) goto end */ \
|
|
if r0 == 0 goto l0_%=; \
|
|
/* bpf_tail_call() */ \
|
|
r3 = 0; \
|
|
r2 = %[map_prog1_tc] ll; \
|
|
r1 = r7; \
|
|
call %[bpf_tail_call]; \
|
|
r0 = 0; \
|
|
r1 = r6; \
|
|
l0_%=: call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tail_call),
|
|
__imm_addr(map_prog1_tc),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: mangle and release sock_or_null")
|
|
__failure __msg("R1 pointer arithmetic on sock_or_null prohibited")
|
|
__naked void and_release_sock_or_null(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
r1 += 5; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: mangle and release sock")
|
|
__failure __msg("R1 pointer arithmetic on sock prohibited")
|
|
__naked void tracking_mangle_and_release_sock(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r1 += 5; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: access member")
|
|
__success __retval(0)
|
|
__naked void reference_tracking_access_member(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r2 = *(u32*)(r0 + 4); \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: write to member")
|
|
__failure __msg("cannot write into sock")
|
|
__naked void reference_tracking_write_to_member(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r1 = r6; \
|
|
r2 = 42 ll; \
|
|
*(u32*)(r1 + %[bpf_sock_mark]) = r2; \
|
|
r1 = r6; \
|
|
l0_%=: call %[bpf_sk_release]; \
|
|
r0 = 0 ll; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(bpf_sock_mark, offsetof(struct bpf_sock, mark)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: invalid 64-bit access of member")
|
|
__failure __msg("invalid sock access off=0 size=8")
|
|
__naked void _64_bit_access_of_member(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r2 = *(u64*)(r0 + 0); \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: access after release")
|
|
__failure __msg("!read_ok")
|
|
__naked void reference_tracking_access_after_release(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r1 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
call %[bpf_sk_release]; \
|
|
r2 = *(u32*)(r1 + 0); \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: direct access for lookup")
|
|
__success __retval(0)
|
|
__naked void tracking_direct_access_for_lookup(void)
|
|
{
|
|
asm volatile (" \
|
|
/* Check that the packet is at least 64B long */\
|
|
r2 = *(u32*)(r1 + %[__sk_buff_data]); \
|
|
r3 = *(u32*)(r1 + %[__sk_buff_data_end]); \
|
|
r0 = r2; \
|
|
r0 += 64; \
|
|
if r0 > r3 goto l0_%=; \
|
|
/* sk = sk_lookup_tcp(ctx, skb->data, ...) */ \
|
|
r3 = %[sizeof_bpf_sock_tuple]; \
|
|
r4 = 0; \
|
|
r5 = 0; \
|
|
call %[bpf_sk_lookup_tcp]; \
|
|
r6 = r0; \
|
|
if r0 == 0 goto l0_%=; \
|
|
r2 = *(u32*)(r0 + 4); \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
|
|
__imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use ptr from bpf_tcp_sock() after release")
|
|
__failure __msg("invalid mem access")
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void bpf_tcp_sock_after_release(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_tcp_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r7 = r0; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u32*)(r7 + %[bpf_tcp_sock_snd_cwnd]); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tcp_sock),
|
|
__imm_const(bpf_tcp_sock_snd_cwnd, offsetof(struct bpf_tcp_sock, snd_cwnd)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use ptr from bpf_sk_fullsock() after release")
|
|
__failure __msg("invalid mem access")
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void bpf_sk_fullsock_after_release(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_sk_fullsock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r7 = r0; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u32*)(r7 + %[bpf_sock_type]); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_fullsock),
|
|
__imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use ptr from bpf_sk_fullsock(tp) after release")
|
|
__failure __msg("invalid mem access")
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void sk_fullsock_tp_after_release(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_tcp_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r1 = r0; \
|
|
call %[bpf_sk_fullsock]; \
|
|
r1 = r6; \
|
|
r6 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
if r6 != 0 goto l2_%=; \
|
|
exit; \
|
|
l2_%=: r0 = *(u32*)(r6 + %[bpf_sock_type]); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_fullsock),
|
|
__imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tcp_sock),
|
|
__imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use sk after bpf_sk_release(tp)")
|
|
__failure __msg("invalid mem access")
|
|
__flag(BPF_F_ANY_ALIGNMENT)
|
|
__naked void after_bpf_sk_release_tp(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_tcp_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u32*)(r6 + %[bpf_sock_type]); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tcp_sock),
|
|
__imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use ptr from bpf_get_listener_sock() after bpf_sk_release(sk)")
|
|
__success __retval(0)
|
|
__naked void after_bpf_sk_release_sk(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_get_listener_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r1 = r6; \
|
|
r6 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u32*)(r6 + %[bpf_sock_src_port]); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_get_listener_sock),
|
|
__imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(bpf_sock_src_port, offsetof(struct bpf_sock, src_port)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: bpf_sk_release(listen_sk)")
|
|
__failure __msg("R1 must be referenced when passed to release function")
|
|
__naked void bpf_sk_release_listen_sk(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_get_listener_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u32*)(r6 + %[bpf_sock_type]); \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_get_listener_sock),
|
|
__imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(bpf_sock_type, offsetof(struct bpf_sock, type)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
/* !bpf_sk_fullsock(sk) is checked but !bpf_tcp_sock(sk) is not checked */
|
|
SEC("tc")
|
|
__description("reference tracking: tp->snd_cwnd after bpf_sk_fullsock(sk) and bpf_tcp_sock(sk)")
|
|
__failure __msg("invalid mem access")
|
|
__naked void and_bpf_tcp_sock_sk(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_sk_fullsock]; \
|
|
r7 = r0; \
|
|
r1 = r6; \
|
|
call %[bpf_tcp_sock]; \
|
|
r8 = r0; \
|
|
if r7 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r0 = *(u32*)(r8 + %[bpf_tcp_sock_snd_cwnd]); \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_fullsock),
|
|
__imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_tcp_sock),
|
|
__imm_const(bpf_tcp_sock_snd_cwnd, offsetof(struct bpf_tcp_sock, snd_cwnd)),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: branch tracking valid pointer null comparison")
|
|
__success __retval(0)
|
|
__naked void tracking_valid_pointer_null_comparison(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
r3 = 1; \
|
|
if r6 != 0 goto l0_%=; \
|
|
r3 = 0; \
|
|
l0_%=: if r6 == 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l1_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: branch tracking valid pointer value comparison")
|
|
__failure __msg("Unreleased reference")
|
|
__naked void tracking_valid_pointer_value_comparison(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" r6 = r0; \
|
|
r3 = 1; \
|
|
if r6 == 0 goto l0_%=; \
|
|
r3 = 0; \
|
|
if r6 == 1234 goto l0_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
l0_%=: exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: bpf_sk_release(btf_tcp_sock)")
|
|
__success
|
|
__retval(0)
|
|
__naked void sk_release_btf_tcp_sock(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_skc_to_tcp_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r1 = r0; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_skc_to_tcp_sock),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("tc")
|
|
__description("reference tracking: use ptr from bpf_skc_to_tcp_sock() after release")
|
|
__failure __msg("invalid mem access")
|
|
__naked void to_tcp_sock_after_release(void)
|
|
{
|
|
asm volatile (
|
|
BPF_SK_LOOKUP(bpf_sk_lookup_tcp)
|
|
" if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r6 = r0; \
|
|
r1 = r0; \
|
|
call %[bpf_skc_to_tcp_sock]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
exit; \
|
|
l1_%=: r7 = r0; \
|
|
r1 = r6; \
|
|
call %[bpf_sk_release]; \
|
|
r0 = *(u8*)(r7 + 0); \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_sk_lookup_tcp),
|
|
__imm(bpf_sk_release),
|
|
__imm(bpf_skc_to_tcp_sock),
|
|
__imm_const(sizeof_bpf_sock_tuple, sizeof(struct bpf_sock_tuple))
|
|
: __clobber_all);
|
|
}
|
|
|
|
SEC("socket")
|
|
__description("reference tracking: try to leak released ptr reg")
|
|
__success __failure_unpriv __msg_unpriv("R8 !read_ok")
|
|
__retval(0)
|
|
__naked void to_leak_released_ptr_reg(void)
|
|
{
|
|
asm volatile (" \
|
|
r0 = 0; \
|
|
*(u32*)(r10 - 4) = r0; \
|
|
r2 = r10; \
|
|
r2 += -4; \
|
|
r1 = %[map_array_48b] ll; \
|
|
call %[bpf_map_lookup_elem]; \
|
|
if r0 != 0 goto l0_%=; \
|
|
exit; \
|
|
l0_%=: r9 = r0; \
|
|
r0 = 0; \
|
|
r1 = %[map_ringbuf] ll; \
|
|
r2 = 8; \
|
|
r3 = 0; \
|
|
call %[bpf_ringbuf_reserve]; \
|
|
if r0 != 0 goto l1_%=; \
|
|
exit; \
|
|
l1_%=: r8 = r0; \
|
|
r1 = r8; \
|
|
r2 = 0; \
|
|
call %[bpf_ringbuf_discard]; \
|
|
r0 = 0; \
|
|
*(u64*)(r9 + 0) = r8; \
|
|
exit; \
|
|
" :
|
|
: __imm(bpf_map_lookup_elem),
|
|
__imm(bpf_ringbuf_discard),
|
|
__imm(bpf_ringbuf_reserve),
|
|
__imm_addr(map_array_48b),
|
|
__imm_addr(map_ringbuf)
|
|
: __clobber_all);
|
|
}
|
|
|
|
char _license[] SEC("license") = "GPL";
|