root/tools/testing/selftests/bpf/progs/test_core_autosize.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020 Facebook */

#include <linux/bpf.h>
#include <stdint.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>

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

/* fields of exactly the same size */
struct test_struct___samesize {
        void *ptr;
        unsigned long long val1;
        unsigned int val2;
        unsigned short val3;
        unsigned char val4;
} __attribute((preserve_access_index));

/* unsigned fields that have to be downsized by libbpf */
struct test_struct___downsize {
        void *ptr;
        unsigned long val1;
        unsigned long val2;
        unsigned long val3;
        unsigned long val4;
        /* total sz: 40 */
} __attribute__((preserve_access_index));

/* fields with signed integers of wrong size, should be rejected */
struct test_struct___signed {
        void *ptr;
        long val1;
        long val2;
        long val3;
        long val4;
} __attribute((preserve_access_index));

/* real layout and sizes according to test's (32-bit) BTF */
struct test_struct___real {
        unsigned int ptr; /* can't use `void *`, it is always 8 byte in BPF target */
        unsigned int val2;
        unsigned long long val1;
        unsigned short val3;
        unsigned char val4;
        unsigned char _pad;
        /* total sz: 20 */
};

struct test_struct___real input = {
        .ptr = 0x01020304,
        .val1 = 0x1020304050607080,
        .val2 = 0x0a0b0c0d,
        .val3 = 0xfeed,
        .val4 = 0xb9,
        ._pad = 0xff, /* make sure no accidental zeros are present */
};

unsigned long long ptr_samesized = 0;
unsigned long long val1_samesized = 0;
unsigned long long val2_samesized = 0;
unsigned long long val3_samesized = 0;
unsigned long long val4_samesized = 0;
struct test_struct___real output_samesized = {};

unsigned long long ptr_downsized = 0;
unsigned long long val1_downsized = 0;
unsigned long long val2_downsized = 0;
unsigned long long val3_downsized = 0;
unsigned long long val4_downsized = 0;
struct test_struct___real output_downsized = {};

unsigned long long ptr_probed = 0;
unsigned long long val1_probed = 0;
unsigned long long val2_probed = 0;
unsigned long long val3_probed = 0;
unsigned long long val4_probed = 0;

unsigned long long ptr_signed = 0;
unsigned long long val1_signed = 0;
unsigned long long val2_signed = 0;
unsigned long long val3_signed = 0;
unsigned long long val4_signed = 0;
struct test_struct___real output_signed = {};

SEC("raw_tp/sys_exit")
int handle_samesize(void *ctx)
{
        struct test_struct___samesize *in = (void *)&input;
        struct test_struct___samesize *out = (void *)&output_samesized;

        ptr_samesized = (unsigned long long)in->ptr;
        val1_samesized = in->val1;
        val2_samesized = in->val2;
        val3_samesized = in->val3;
        val4_samesized = in->val4;

        out->ptr = in->ptr;
        out->val1 = in->val1;
        out->val2 = in->val2;
        out->val3 = in->val3;
        out->val4 = in->val4;

        return 0;
}

SEC("raw_tp/sys_exit")
int handle_downsize(void *ctx)
{
        struct test_struct___downsize *in = (void *)&input;
        struct test_struct___downsize *out = (void *)&output_downsized;

        ptr_downsized = (unsigned long long)in->ptr;
        val1_downsized = in->val1;
        val2_downsized = in->val2;
        val3_downsized = in->val3;
        val4_downsized = in->val4;

        out->ptr = in->ptr;
        out->val1 = in->val1;
        out->val2 = in->val2;
        out->val3 = in->val3;
        out->val4 = in->val4;

        return 0;
}

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define bpf_core_read_int bpf_core_read
#else
#define bpf_core_read_int(dst, sz, src) ({ \
        /* Prevent "subtraction from stack pointer prohibited" */ \
        volatile long __off = sizeof(*dst) - (sz); \
        bpf_core_read((char *)(dst) + __off, sz, src); \
})
#endif

SEC("raw_tp/sys_enter")
int handle_probed(void *ctx)
{
        struct test_struct___downsize *in = (void *)&input;
        __u64 tmp;

        tmp = 0;
        bpf_core_read_int(&tmp, bpf_core_field_size(in->ptr), &in->ptr);
        ptr_probed = tmp;

        tmp = 0;
        bpf_core_read_int(&tmp, bpf_core_field_size(in->val1), &in->val1);
        val1_probed = tmp;

        tmp = 0;
        bpf_core_read_int(&tmp, bpf_core_field_size(in->val2), &in->val2);
        val2_probed = tmp;

        tmp = 0;
        bpf_core_read_int(&tmp, bpf_core_field_size(in->val3), &in->val3);
        val3_probed = tmp;

        tmp = 0;
        bpf_core_read_int(&tmp, bpf_core_field_size(in->val4), &in->val4);
        val4_probed = tmp;

        return 0;
}

SEC("raw_tp/sys_enter")
int handle_signed(void *ctx)
{
        struct test_struct___signed *in = (void *)&input;
        struct test_struct___signed *out = (void *)&output_signed;

        val2_signed = in->val2;
        val3_signed = in->val3;
        val4_signed = in->val4;

        out->val2= in->val2;
        out->val3= in->val3;
        out->val4= in->val4;

        return 0;
}