mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

The underlying lookup_user_key() function uses a signed 32 bit integer for key serial numbers because legitimate serial numbers are positive (and > 3) and keyrings are negative. Using a u32 for the keyring in the bpf function doesn't currently cause any conversion problems but will start to trip the signed to unsigned conversion warnings when the kernel enables them, so convert the argument to signed (and update the tests accordingly) before it acquires more users. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com> Link: https://lore.kernel.org/r/84cdb0775254d297d75e21f577089f64abdfbd28.camel@HansenPartnership.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1495 lines
37 KiB
C
1495 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(__s32 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] would lead to reference leak")
|
|
__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] would lead to reference leak")
|
|
__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";
|