selftests/bpf: add CO-RE relocs nesting tests
Add a bunch of test validating correct handling of nested structs/unions. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Acked-by: Song Liu <songliubraving@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
		
							parent
							
								
									002d3afce6
								
							
						
					
					
						commit
						ec6438a988
					
				@ -28,6 +28,29 @@
 | 
			
		||||
	.fails = true,							\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) {	\
 | 
			
		||||
	.a = { .a = { .a = 42 } },					\
 | 
			
		||||
	.b = { .b = { .b = 0xc001 } },					\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NESTING_CASE_COMMON(name)					\
 | 
			
		||||
	.case_name = #name,						\
 | 
			
		||||
	.bpf_obj_file = "test_core_reloc_nesting.o",			\
 | 
			
		||||
	.btf_src_file = "btf__core_reloc_" #name ".o"
 | 
			
		||||
 | 
			
		||||
#define NESTING_CASE(name) {						\
 | 
			
		||||
	NESTING_CASE_COMMON(name),					\
 | 
			
		||||
	.input = NESTING_DATA(core_reloc_##name),			\
 | 
			
		||||
	.input_len = sizeof(struct core_reloc_##name),			\
 | 
			
		||||
	.output = NESTING_DATA(core_reloc_nesting),			\
 | 
			
		||||
	.output_len = sizeof(struct core_reloc_nesting)			\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define NESTING_ERR_CASE(name) {					\
 | 
			
		||||
	NESTING_CASE_COMMON(name),					\
 | 
			
		||||
	.fails = true,							\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct core_reloc_test_case {
 | 
			
		||||
	const char *case_name;
 | 
			
		||||
	const char *bpf_obj_file;
 | 
			
		||||
@ -57,6 +80,22 @@ static struct core_reloc_test_case test_cases[] = {
 | 
			
		||||
	FLAVORS_CASE(flavors),
 | 
			
		||||
 | 
			
		||||
	FLAVORS_ERR_CASE(flavors__err_wrong_name),
 | 
			
		||||
 | 
			
		||||
	/* various struct/enum nesting and resolution scenarios */
 | 
			
		||||
	NESTING_CASE(nesting),
 | 
			
		||||
	NESTING_CASE(nesting___anon_embed),
 | 
			
		||||
	NESTING_CASE(nesting___struct_union_mixup),
 | 
			
		||||
	NESTING_CASE(nesting___extra_nesting),
 | 
			
		||||
	NESTING_CASE(nesting___dup_compat_types),
 | 
			
		||||
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_missing_field),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_array_field),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_missing_container),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_nonstruct_container),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_array_container),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_dup_incompat_types),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_partial_match_dups),
 | 
			
		||||
	NESTING_ERR_CASE(nesting___err_too_deep),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct data {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___anon_embed x) {}
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f1(struct core_reloc_nesting___dup_compat_types x) {}
 | 
			
		||||
void f2(struct core_reloc_nesting___dup_compat_types__2 x) {}
 | 
			
		||||
void f3(struct core_reloc_nesting___dup_compat_types__3 x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_array_container x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_array_field x) {}
 | 
			
		||||
@ -0,0 +1,4 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {}
 | 
			
		||||
void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_missing_container x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_missing_field x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_nonstruct_container x) {}
 | 
			
		||||
@ -0,0 +1,4 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {}
 | 
			
		||||
void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___err_too_deep x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___extra_nesting x) {}
 | 
			
		||||
@ -0,0 +1,3 @@
 | 
			
		||||
#include "core_reloc_types.h"
 | 
			
		||||
 | 
			
		||||
void f(struct core_reloc_nesting___struct_union_mixup x) {}
 | 
			
		||||
@ -13,3 +13,296 @@ struct core_reloc_flavors__err_wrong_name {
 | 
			
		||||
	int b;
 | 
			
		||||
	int c;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * NESTING
 | 
			
		||||
 */
 | 
			
		||||
/* original set up, used to record relocations in BPF program */
 | 
			
		||||
struct core_reloc_nesting_substruct {
 | 
			
		||||
	int a;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
union core_reloc_nesting_subunion {
 | 
			
		||||
	int b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting {
 | 
			
		||||
	union {
 | 
			
		||||
		struct core_reloc_nesting_substruct a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		union core_reloc_nesting_subunion b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* inlined anonymous struct/union instead of named structs in original */
 | 
			
		||||
struct core_reloc_nesting___anon_embed {
 | 
			
		||||
	int __just_for_padding;
 | 
			
		||||
	union {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		union {
 | 
			
		||||
			int b;
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* different mix of nested structs/unions than in original */
 | 
			
		||||
struct core_reloc_nesting___struct_union_mixup {
 | 
			
		||||
	int __a;
 | 
			
		||||
	struct {
 | 
			
		||||
		int __a;
 | 
			
		||||
		union {
 | 
			
		||||
			char __a;
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	int __b;
 | 
			
		||||
	union {
 | 
			
		||||
		int __b;
 | 
			
		||||
		union {
 | 
			
		||||
			char __b;
 | 
			
		||||
			int b;
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
 | 
			
		||||
struct core_reloc_nesting___extra_nesting {
 | 
			
		||||
	int __padding;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			struct {
 | 
			
		||||
				struct {
 | 
			
		||||
					union {
 | 
			
		||||
						int a;
 | 
			
		||||
					} a;
 | 
			
		||||
				};
 | 
			
		||||
			};
 | 
			
		||||
		} a;
 | 
			
		||||
		int __some_more;
 | 
			
		||||
		struct {
 | 
			
		||||
			union {
 | 
			
		||||
				union {
 | 
			
		||||
					union {
 | 
			
		||||
						struct {
 | 
			
		||||
							int b;
 | 
			
		||||
						};
 | 
			
		||||
					} b;
 | 
			
		||||
				};
 | 
			
		||||
			} b;
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* three flavors of same struct with different structure but same layout for
 | 
			
		||||
 * a.a.a and b.b.b, thus successfully resolved and relocatable */
 | 
			
		||||
struct core_reloc_nesting___dup_compat_types {
 | 
			
		||||
	char __just_for_padding;
 | 
			
		||||
	/* 3 more bytes of padding */
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a; /* offset 4 */
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	long long __more_padding;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b; /* offset 16 */
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting___dup_compat_types__2 {
 | 
			
		||||
	int __aligned_padding;
 | 
			
		||||
	struct {
 | 
			
		||||
		int __trickier_noop[0];
 | 
			
		||||
		struct {
 | 
			
		||||
			char __some_more_noops[0];
 | 
			
		||||
			int a; /* offset 4 */
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	int __more_padding;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			struct {
 | 
			
		||||
				int __critical_padding;
 | 
			
		||||
				int b; /* offset 16 */
 | 
			
		||||
			} b;
 | 
			
		||||
			int __does_not_matter;
 | 
			
		||||
		};
 | 
			
		||||
	} b;
 | 
			
		||||
	int __more_irrelevant_stuff;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting___dup_compat_types__3 {
 | 
			
		||||
	char __correct_padding[4];
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a; /* offset 4 */
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	/* 8 byte padding due to next struct's alignment */
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b;
 | 
			
		||||
		} b;
 | 
			
		||||
	} b __attribute__((aligned(16)));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* b.b.b field is missing */
 | 
			
		||||
struct core_reloc_nesting___err_missing_field {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int x;
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* b.b.b field is an array of integers instead of plain int */
 | 
			
		||||
struct core_reloc_nesting___err_array_field {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b[1];
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* middle b container is missing */
 | 
			
		||||
struct core_reloc_nesting___err_missing_container {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		int x;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* middle b container is referenced through pointer instead of being embedded */
 | 
			
		||||
struct core_reloc_nesting___err_nonstruct_container {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b;
 | 
			
		||||
		} *b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* middle b container is an array of structs instead of plain struct */
 | 
			
		||||
struct core_reloc_nesting___err_array_container {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b;
 | 
			
		||||
		} b[1];
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* two flavors of same struct with incompatible layout for b.b.b */
 | 
			
		||||
struct core_reloc_nesting___err_dup_incompat_types__1 {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a; /* offset 0 */
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b; /* offset 4 */
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting___err_dup_incompat_types__2 {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a; /* offset 0 */
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	int __extra_padding;
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b; /* offset 8 (!) */
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
 | 
			
		||||
struct core_reloc_nesting___err_partial_match_dups__a {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting___err_partial_match_dups__b {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int b;
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting___err_too_deep {
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			int a;
 | 
			
		||||
		} a;
 | 
			
		||||
	} a;
 | 
			
		||||
	/* 65 levels of nestedness for b.b.b */
 | 
			
		||||
	struct {
 | 
			
		||||
		struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
			struct { struct { struct { struct { struct {
 | 
			
		||||
				/* this one is one too much */
 | 
			
		||||
				struct {
 | 
			
		||||
					int b;
 | 
			
		||||
				};
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
			}; }; }; }; };
 | 
			
		||||
		} b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										46
									
								
								tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								tools/testing/selftests/bpf/progs/test_core_reloc_nesting.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-2.0
 | 
			
		||||
// Copyright (c) 2019 Facebook
 | 
			
		||||
 | 
			
		||||
#include <linux/bpf.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include "bpf_helpers.h"
 | 
			
		||||
 | 
			
		||||
char _license[] SEC("license") = "GPL";
 | 
			
		||||
 | 
			
		||||
static volatile struct data {
 | 
			
		||||
	char in[256];
 | 
			
		||||
	char out[256];
 | 
			
		||||
} data;
 | 
			
		||||
 | 
			
		||||
struct core_reloc_nesting_substruct {
 | 
			
		||||
	int a;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
union core_reloc_nesting_subunion {
 | 
			
		||||
	int b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* int a.a.a and b.b.b accesses */
 | 
			
		||||
struct core_reloc_nesting {
 | 
			
		||||
	union {
 | 
			
		||||
		struct core_reloc_nesting_substruct a;
 | 
			
		||||
	} a;
 | 
			
		||||
	struct {
 | 
			
		||||
		union core_reloc_nesting_subunion b;
 | 
			
		||||
	} b;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SEC("raw_tracepoint/sys_enter")
 | 
			
		||||
int test_core_nesting(void *ctx)
 | 
			
		||||
{
 | 
			
		||||
	struct core_reloc_nesting *in = (void *)&data.in;
 | 
			
		||||
	struct core_reloc_nesting *out = (void *)&data.out;
 | 
			
		||||
 | 
			
		||||
	if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a))
 | 
			
		||||
		return 1;
 | 
			
		||||
	if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b))
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user