selftest/bpf: Add relocatable bitfield reading tests

Add a bunch of selftests verifying correctness of relocatable bitfield reading
support in libbpf. Both bpf_probe_read()-based and direct read-based bitfield
macros are tested. core_reloc.c "test_harness" is extended to support raw
tracepoint and new typed raw tracepoints as test BPF program types.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20191101222810.1246166-5-andriin@fb.com
This commit is contained in:
Andrii Nakryiko 2019-11-01 15:28:09 -07:00 committed by Daniel Borkmann
parent 94f060e984
commit 8b1cb1c960
9 changed files with 294 additions and 2 deletions

View file

@ -189,6 +189,42 @@
.fails = true, \
}
#define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \
.case_name = test_name_prefix#name, \
.bpf_obj_file = objfile, \
.btf_src_file = "btf__core_reloc_" #name ".o"
#define BITFIELDS_CASE(name, ...) { \
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
"direct:", name), \
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
.input_len = sizeof(struct core_reloc_##name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
__VA_ARGS__, \
.output_len = sizeof(struct core_reloc_bitfields_output), \
}, { \
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
"probed:", name), \
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
.input_len = sizeof(struct core_reloc_##name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
__VA_ARGS__, \
.output_len = sizeof(struct core_reloc_bitfields_output), \
.direct_raw_tp = true, \
}
#define BITFIELDS_ERR_CASE(name) { \
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
"probed:", name), \
.fails = true, \
}, { \
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
"direct:", name), \
.direct_raw_tp = true, \
.fails = true, \
}
struct core_reloc_test_case {
const char *case_name;
const char *bpf_obj_file;
@ -199,6 +235,7 @@ struct core_reloc_test_case {
int output_len;
bool fails;
bool relaxed_core_relocs;
bool direct_raw_tp;
};
static struct core_reloc_test_case test_cases[] = {
@ -352,6 +389,40 @@ static struct core_reloc_test_case test_cases[] = {
EXISTENCE_ERR_CASE(existence__err_arr_kind),
EXISTENCE_ERR_CASE(existence__err_arr_value_type),
EXISTENCE_ERR_CASE(existence__err_struct_type),
/* bitfield relocation checks */
BITFIELDS_CASE(bitfields, {
.ub1 = 1,
.ub2 = 2,
.ub7 = 96,
.sb4 = -7,
.sb20 = -0x76543,
.u32 = 0x80000000,
.s32 = -0x76543210,
}),
BITFIELDS_CASE(bitfields___bit_sz_change, {
.ub1 = 6,
.ub2 = 0xABCDE,
.ub7 = 1,
.sb4 = -1,
.sb20 = -0x17654321,
.u32 = 0xBEEF,
.s32 = -0x3FEDCBA987654321,
}),
BITFIELDS_CASE(bitfields___bitfield_vs_int, {
.ub1 = 0xFEDCBA9876543210,
.ub2 = 0xA6,
.ub7 = -0x7EDCBA987654321,
.sb4 = -0x6123456789ABCDE,
.sb20 = 0xD00D,
.u32 = -0x76543,
.s32 = 0x0ADEADBEEFBADB0B,
}),
BITFIELDS_CASE(bitfields___just_big_enough, {
.ub1 = 0xF,
.ub2 = 0x0812345678FEDCBA,
}),
BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield),
};
struct data {
@ -361,9 +432,9 @@ struct data {
void test_core_reloc(void)
{
const char *probe_name = "raw_tracepoint/sys_enter";
struct bpf_object_load_attr load_attr = {};
struct core_reloc_test_case *test_case;
const char *tp_name, *probe_name;
int err, duration = 0, i, equal;
struct bpf_link *link = NULL;
struct bpf_map *data_map;
@ -387,6 +458,15 @@ void test_core_reloc(void)
test_case->bpf_obj_file, PTR_ERR(obj)))
continue;
/* for typed raw tracepoints, NULL should be specified */
if (test_case->direct_raw_tp) {
probe_name = "tp_btf/sys_enter";
tp_name = NULL;
} else {
probe_name = "raw_tracepoint/sys_enter";
tp_name = "sys_enter";
}
prog = bpf_object__find_program_by_title(obj, probe_name);
if (CHECK(!prog, "find_probe",
"prog '%s' not found\n", probe_name))
@ -407,7 +487,7 @@ void test_core_reloc(void)
goto cleanup;
}
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
link = bpf_program__attach_raw_tracepoint(prog, tp_name);
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
PTR_ERR(link)))
goto cleanup;

View file

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_bitfields x) {}

View file

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_bitfields___bit_sz_change x) {}

View file

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_bitfields___bitfield_vs_int x) {}

View file

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_bitfields___err_too_big_bitfield x) {}

View file

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_bitfields___just_big_enough x) {}

View file

@ -662,3 +662,75 @@ struct core_reloc_existence___err_wrong_arr_value_type {
struct core_reloc_existence___err_wrong_struct_type {
int s;
};
/*
* BITFIELDS
*/
/* bitfield read results, all as plain integers */
struct core_reloc_bitfields_output {
int64_t ub1;
int64_t ub2;
int64_t ub7;
int64_t sb4;
int64_t sb20;
int64_t u32;
int64_t s32;
};
struct core_reloc_bitfields {
/* unsigned bitfields */
uint8_t ub1: 1;
uint8_t ub2: 2;
uint32_t ub7: 7;
/* signed bitfields */
int8_t sb4: 4;
int32_t sb20: 20;
/* non-bitfields */
uint32_t u32;
int32_t s32;
};
/* different bit sizes (both up and down) */
struct core_reloc_bitfields___bit_sz_change {
/* unsigned bitfields */
uint16_t ub1: 3; /* 1 -> 3 */
uint32_t ub2: 20; /* 2 -> 20 */
uint8_t ub7: 1; /* 7 -> 1 */
/* signed bitfields */
int8_t sb4: 1; /* 4 -> 1 */
int32_t sb20: 30; /* 20 -> 30 */
/* non-bitfields */
uint16_t u32; /* 32 -> 16 */
int64_t s32; /* 32 -> 64 */
};
/* turn bitfield into non-bitfield and vice versa */
struct core_reloc_bitfields___bitfield_vs_int {
uint64_t ub1; /* 3 -> 64 non-bitfield */
uint8_t ub2; /* 20 -> 8 non-bitfield */
int64_t ub7; /* 7 -> 64 non-bitfield signed */
int64_t sb4; /* 4 -> 64 non-bitfield signed */
uint64_t sb20; /* 20 -> 16 non-bitfield unsigned */
int32_t u32: 20; /* 32 non-bitfield -> 20 bitfield */
uint64_t s32: 60; /* 32 non-bitfield -> 60 bitfield */
};
struct core_reloc_bitfields___just_big_enough {
uint64_t ub1: 4;
uint64_t ub2: 60; /* packed tightly */
uint32_t ub7;
uint32_t sb4;
uint32_t sb20;
uint32_t u32;
uint32_t s32;
} __attribute__((packed)) ;
struct core_reloc_bitfields___err_too_big_bitfield {
uint64_t ub1: 4;
uint64_t ub2: 61; /* packed tightly */
uint32_t ub7;
uint32_t sb4;
uint32_t sb20;
uint32_t u32;
uint32_t s32;
} __attribute__((packed)) ;

View file

@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
#include "bpf_core_read.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_bitfields {
/* unsigned bitfields */
uint8_t ub1: 1;
uint8_t ub2: 2;
uint32_t ub7: 7;
/* signed bitfields */
int8_t sb4: 4;
int32_t sb20: 20;
/* non-bitfields */
uint32_t u32;
int32_t s32;
};
/* bitfield read results, all as plain integers */
struct core_reloc_bitfields_output {
int64_t ub1;
int64_t ub2;
int64_t ub7;
int64_t sb4;
int64_t sb20;
int64_t u32;
int64_t s32;
};
struct pt_regs;
struct trace_sys_enter {
struct pt_regs *regs;
long id;
};
SEC("tp_btf/sys_enter")
int test_core_bitfields_direct(void *ctx)
{
struct core_reloc_bitfields *in = (void *)&data.in;
struct core_reloc_bitfields_output *out = (void *)&data.out;
out->ub1 = BPF_CORE_READ_BITFIELD(in, ub1);
out->ub2 = BPF_CORE_READ_BITFIELD(in, ub2);
out->ub7 = BPF_CORE_READ_BITFIELD(in, ub7);
out->sb4 = BPF_CORE_READ_BITFIELD(in, sb4);
out->sb20 = BPF_CORE_READ_BITFIELD(in, sb20);
out->u32 = BPF_CORE_READ_BITFIELD(in, u32);
out->s32 = BPF_CORE_READ_BITFIELD(in, s32);
return 0;
}

View file

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
#include "bpf_core_read.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_bitfields {
/* unsigned bitfields */
uint8_t ub1: 1;
uint8_t ub2: 2;
uint32_t ub7: 7;
/* signed bitfields */
int8_t sb4: 4;
int32_t sb20: 20;
/* non-bitfields */
uint32_t u32;
int32_t s32;
};
/* bitfield read results, all as plain integers */
struct core_reloc_bitfields_output {
int64_t ub1;
int64_t ub2;
int64_t ub7;
int64_t sb4;
int64_t sb20;
int64_t u32;
int64_t s32;
};
#define TRANSFER_BITFIELD(in, out, field) \
if (BPF_CORE_READ_BITFIELD_PROBED(in, field, &res)) \
return 1; \
out->field = res
SEC("raw_tracepoint/sys_enter")
int test_core_bitfields(void *ctx)
{
struct core_reloc_bitfields *in = (void *)&data.in;
struct core_reloc_bitfields_output *out = (void *)&data.out;
uint64_t res;
TRANSFER_BITFIELD(in, out, ub1);
TRANSFER_BITFIELD(in, out, ub2);
TRANSFER_BITFIELD(in, out, ub7);
TRANSFER_BITFIELD(in, out, sb4);
TRANSFER_BITFIELD(in, out, sb20);
TRANSFER_BITFIELD(in, out, u32);
TRANSFER_BITFIELD(in, out, s32);
return 0;
}