162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2014-2016 by Open Source Security, Inc., Brad Spengler <spender@grsecurity.net>
362306a36Sopenharmony_ci *                   and PaX Team <pageexec@freemail.hu>
462306a36Sopenharmony_ci * Licensed under the GPL v2
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Note: the choice of the license means that the compilation process is
762306a36Sopenharmony_ci *       NOT 'eligible' as defined by gcc's library exception to the GPL v3,
862306a36Sopenharmony_ci *       but for the kernel it doesn't matter since it doesn't link against
962306a36Sopenharmony_ci *       any of the gcc libraries
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Usage:
1262306a36Sopenharmony_ci * $ # for 4.5/4.6/C based 4.7
1362306a36Sopenharmony_ci * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
1462306a36Sopenharmony_ci * $ # for C++ based 4.7/4.8+
1562306a36Sopenharmony_ci * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o randomize_layout_plugin.so randomize_layout_plugin.c
1662306a36Sopenharmony_ci * $ gcc -fplugin=./randomize_layout_plugin.so test.c -O2
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "gcc-common.h"
2062306a36Sopenharmony_ci#include "randomize_layout_seed.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#if BUILDING_GCC_MAJOR < 4 || (BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR < 7)
2362306a36Sopenharmony_ci#error "The RANDSTRUCT plugin requires GCC 4.7 or newer."
2462306a36Sopenharmony_ci#endif
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define ORIG_TYPE_NAME(node) \
2762306a36Sopenharmony_ci	(TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define INFORM(loc, msg, ...)	inform(loc, "randstruct: " msg, ##__VA_ARGS__)
3062306a36Sopenharmony_ci#define MISMATCH(loc, how, ...)	INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__)
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci__visible int plugin_is_GPL_compatible;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int performance_mode;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct plugin_info randomize_layout_plugin_info = {
3762306a36Sopenharmony_ci	.version	= PLUGIN_VERSION,
3862306a36Sopenharmony_ci	.help		= "disable\t\t\tdo not activate plugin\n"
3962306a36Sopenharmony_ci			  "performance-mode\tenable cacheline-aware layout randomization\n"
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* from old Linux dcache.h */
4362306a36Sopenharmony_cistatic inline unsigned long
4462306a36Sopenharmony_cipartial_name_hash(unsigned long c, unsigned long prevhash)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return (prevhash + (c << 4) + (c >> 4)) * 11;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_cistatic inline unsigned int
4962306a36Sopenharmony_ciname_hash(const unsigned char *name)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	unsigned long hash = 0;
5262306a36Sopenharmony_ci	unsigned int len = strlen((const char *)name);
5362306a36Sopenharmony_ci	while (len--)
5462306a36Sopenharmony_ci		hash = partial_name_hash(*name++, hash);
5562306a36Sopenharmony_ci	return (unsigned int)hash;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic tree handle_randomize_layout_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	tree type;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	*no_add_attrs = true;
6362306a36Sopenharmony_ci	if (TREE_CODE(*node) == FUNCTION_DECL) {
6462306a36Sopenharmony_ci		error("%qE attribute does not apply to functions (%qF)", name, *node);
6562306a36Sopenharmony_ci		return NULL_TREE;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (TREE_CODE(*node) == PARM_DECL) {
6962306a36Sopenharmony_ci		error("%qE attribute does not apply to function parameters (%qD)", name, *node);
7062306a36Sopenharmony_ci		return NULL_TREE;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (TREE_CODE(*node) == VAR_DECL) {
7462306a36Sopenharmony_ci		error("%qE attribute does not apply to variables (%qD)", name, *node);
7562306a36Sopenharmony_ci		return NULL_TREE;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (TYPE_P(*node)) {
7962306a36Sopenharmony_ci		type = *node;
8062306a36Sopenharmony_ci	} else {
8162306a36Sopenharmony_ci		gcc_assert(TREE_CODE(*node) == TYPE_DECL);
8262306a36Sopenharmony_ci		type = TREE_TYPE(*node);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (TREE_CODE(type) != RECORD_TYPE) {
8662306a36Sopenharmony_ci		error("%qE attribute used on %qT applies to struct types only", name, type);
8762306a36Sopenharmony_ci		return NULL_TREE;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(type))) {
9162306a36Sopenharmony_ci		error("%qE attribute is already applied to the type %qT", name, type);
9262306a36Sopenharmony_ci		return NULL_TREE;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	*no_add_attrs = false;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return NULL_TREE;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* set on complete types that we don't need to inspect further at all */
10162306a36Sopenharmony_cistatic tree handle_randomize_considered_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	*no_add_attrs = false;
10462306a36Sopenharmony_ci	return NULL_TREE;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * set on types that we've performed a shuffle on, to prevent re-shuffling
10962306a36Sopenharmony_ci * this does not preclude us from inspecting its fields for potential shuffles
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic tree handle_randomize_performed_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	*no_add_attrs = false;
11462306a36Sopenharmony_ci	return NULL_TREE;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * 64bit variant of Bob Jenkins' public domain PRNG
11962306a36Sopenharmony_ci * 256 bits of internal state
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_citypedef unsigned long long u64;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_citypedef struct ranctx { u64 a; u64 b; u64 c; u64 d; } ranctx;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci#define rot(x,k) (((x)<<(k))|((x)>>(64-(k))))
12762306a36Sopenharmony_cistatic u64 ranval(ranctx *x) {
12862306a36Sopenharmony_ci	u64 e = x->a - rot(x->b, 7);
12962306a36Sopenharmony_ci	x->a = x->b ^ rot(x->c, 13);
13062306a36Sopenharmony_ci	x->b = x->c + rot(x->d, 37);
13162306a36Sopenharmony_ci	x->c = x->d + e;
13262306a36Sopenharmony_ci	x->d = e + x->a;
13362306a36Sopenharmony_ci	return x->d;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic void raninit(ranctx *x, u64 *seed) {
13762306a36Sopenharmony_ci	int i;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	x->a = seed[0];
14062306a36Sopenharmony_ci	x->b = seed[1];
14162306a36Sopenharmony_ci	x->c = seed[2];
14262306a36Sopenharmony_ci	x->d = seed[3];
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (i=0; i < 30; ++i)
14562306a36Sopenharmony_ci		(void)ranval(x);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic u64 shuffle_seed[4];
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistruct partition_group {
15162306a36Sopenharmony_ci	tree tree_start;
15262306a36Sopenharmony_ci	unsigned long start;
15362306a36Sopenharmony_ci	unsigned long length;
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic void partition_struct(tree *fields, unsigned long length, struct partition_group *size_groups, unsigned long *num_groups)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	unsigned long i;
15962306a36Sopenharmony_ci	unsigned long accum_size = 0;
16062306a36Sopenharmony_ci	unsigned long accum_length = 0;
16162306a36Sopenharmony_ci	unsigned long group_idx = 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	gcc_assert(length < INT_MAX);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	memset(size_groups, 0, sizeof(struct partition_group) * length);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < length; i++) {
16862306a36Sopenharmony_ci		if (size_groups[group_idx].tree_start == NULL_TREE) {
16962306a36Sopenharmony_ci			size_groups[group_idx].tree_start = fields[i];
17062306a36Sopenharmony_ci			size_groups[group_idx].start = i;
17162306a36Sopenharmony_ci			accum_length = 0;
17262306a36Sopenharmony_ci			accum_size = 0;
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci		accum_size += (unsigned long)int_size_in_bytes(TREE_TYPE(fields[i]));
17562306a36Sopenharmony_ci		accum_length++;
17662306a36Sopenharmony_ci		if (accum_size >= 64) {
17762306a36Sopenharmony_ci			size_groups[group_idx].length = accum_length;
17862306a36Sopenharmony_ci			accum_length = 0;
17962306a36Sopenharmony_ci			group_idx++;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (size_groups[group_idx].tree_start != NULL_TREE &&
18462306a36Sopenharmony_ci	    !size_groups[group_idx].length) {
18562306a36Sopenharmony_ci		size_groups[group_idx].length = accum_length;
18662306a36Sopenharmony_ci		group_idx++;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	*num_groups = group_idx;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic void performance_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	unsigned long i, x, index;
19562306a36Sopenharmony_ci	struct partition_group size_group[length];
19662306a36Sopenharmony_ci	unsigned long num_groups = 0;
19762306a36Sopenharmony_ci	unsigned long randnum;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	partition_struct(newtree, length, (struct partition_group *)&size_group, &num_groups);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* FIXME: this group shuffle is currently a no-op. */
20262306a36Sopenharmony_ci	for (i = num_groups - 1; i > 0; i--) {
20362306a36Sopenharmony_ci		struct partition_group tmp;
20462306a36Sopenharmony_ci		randnum = ranval(prng_state) % (i + 1);
20562306a36Sopenharmony_ci		tmp = size_group[i];
20662306a36Sopenharmony_ci		size_group[i] = size_group[randnum];
20762306a36Sopenharmony_ci		size_group[randnum] = tmp;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	for (x = 0; x < num_groups; x++) {
21162306a36Sopenharmony_ci		for (index = size_group[x].length - 1; index > 0; index--) {
21262306a36Sopenharmony_ci			tree tmp;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci			i = size_group[x].start + index;
21562306a36Sopenharmony_ci			if (DECL_BIT_FIELD_TYPE(newtree[i]))
21662306a36Sopenharmony_ci				continue;
21762306a36Sopenharmony_ci			randnum = ranval(prng_state) % (index + 1);
21862306a36Sopenharmony_ci			randnum += size_group[x].start;
21962306a36Sopenharmony_ci			// we could handle this case differently if desired
22062306a36Sopenharmony_ci			if (DECL_BIT_FIELD_TYPE(newtree[randnum]))
22162306a36Sopenharmony_ci				continue;
22262306a36Sopenharmony_ci			tmp = newtree[i];
22362306a36Sopenharmony_ci			newtree[i] = newtree[randnum];
22462306a36Sopenharmony_ci			newtree[randnum] = tmp;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void full_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	unsigned long i, randnum;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	for (i = length - 1; i > 0; i--) {
23462306a36Sopenharmony_ci		tree tmp;
23562306a36Sopenharmony_ci		randnum = ranval(prng_state) % (i + 1);
23662306a36Sopenharmony_ci		tmp = newtree[i];
23762306a36Sopenharmony_ci		newtree[i] = newtree[randnum];
23862306a36Sopenharmony_ci		newtree[randnum] = tmp;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/* modern in-place Fisher-Yates shuffle */
24362306a36Sopenharmony_cistatic void shuffle(const_tree type, tree *newtree, unsigned long length)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned long i;
24662306a36Sopenharmony_ci	u64 seed[4];
24762306a36Sopenharmony_ci	ranctx prng_state;
24862306a36Sopenharmony_ci	const unsigned char *structname;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (length == 0)
25162306a36Sopenharmony_ci		return;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	gcc_assert(TREE_CODE(type) == RECORD_TYPE);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	structname = ORIG_TYPE_NAME(type);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci#ifdef __DEBUG_PLUGIN
25862306a36Sopenharmony_ci	fprintf(stderr, "Shuffling struct %s %p\n", (const char *)structname, type);
25962306a36Sopenharmony_ci#ifdef __DEBUG_VERBOSE
26062306a36Sopenharmony_ci	debug_tree((tree)type);
26162306a36Sopenharmony_ci#endif
26262306a36Sopenharmony_ci#endif
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
26562306a36Sopenharmony_ci		seed[i] = shuffle_seed[i];
26662306a36Sopenharmony_ci		seed[i] ^= name_hash(structname);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	raninit(&prng_state, (u64 *)&seed);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (performance_mode)
27262306a36Sopenharmony_ci		performance_shuffle(newtree, length, &prng_state);
27362306a36Sopenharmony_ci	else
27462306a36Sopenharmony_ci		full_shuffle(newtree, length, &prng_state);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic bool is_flexible_array(const_tree field)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	const_tree fieldtype;
28062306a36Sopenharmony_ci	const_tree typesize;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	fieldtype = TREE_TYPE(field);
28362306a36Sopenharmony_ci	typesize = TYPE_SIZE(fieldtype);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (TREE_CODE(fieldtype) != ARRAY_TYPE)
28662306a36Sopenharmony_ci		return false;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/* size of type is represented in bits */
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (typesize == NULL_TREE && TYPE_DOMAIN(fieldtype) != NULL_TREE &&
29162306a36Sopenharmony_ci	    TYPE_MAX_VALUE(TYPE_DOMAIN(fieldtype)) == NULL_TREE)
29262306a36Sopenharmony_ci		return true;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return false;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic int relayout_struct(tree type)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	unsigned long num_fields = (unsigned long)list_length(TYPE_FIELDS(type));
30062306a36Sopenharmony_ci	unsigned long shuffle_length = num_fields;
30162306a36Sopenharmony_ci	tree field;
30262306a36Sopenharmony_ci	tree newtree[num_fields];
30362306a36Sopenharmony_ci	unsigned long i;
30462306a36Sopenharmony_ci	tree list;
30562306a36Sopenharmony_ci	tree variant;
30662306a36Sopenharmony_ci	tree main_variant;
30762306a36Sopenharmony_ci	expanded_location xloc;
30862306a36Sopenharmony_ci	bool has_flexarray = false;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (TYPE_FIELDS(type) == NULL_TREE)
31162306a36Sopenharmony_ci		return 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (num_fields < 2)
31462306a36Sopenharmony_ci		return 0;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	gcc_assert(TREE_CODE(type) == RECORD_TYPE);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	gcc_assert(num_fields < INT_MAX);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)) ||
32162306a36Sopenharmony_ci	    lookup_attribute("no_randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))))
32262306a36Sopenharmony_ci		return 0;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	/* Workaround for 3rd-party VirtualBox source that we can't modify ourselves */
32562306a36Sopenharmony_ci	if (!strcmp((const char *)ORIG_TYPE_NAME(type), "INTNETTRUNKFACTORY") ||
32662306a36Sopenharmony_ci	    !strcmp((const char *)ORIG_TYPE_NAME(type), "RAWPCIFACTORY"))
32762306a36Sopenharmony_ci		return 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* throw out any structs in uapi */
33062306a36Sopenharmony_ci	xloc = expand_location(DECL_SOURCE_LOCATION(TYPE_FIELDS(type)));
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (strstr(xloc.file, "/uapi/"))
33362306a36Sopenharmony_ci		error(G_("attempted to randomize userland API struct %s"), ORIG_TYPE_NAME(type));
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	for (field = TYPE_FIELDS(type), i = 0; field; field = TREE_CHAIN(field), i++) {
33662306a36Sopenharmony_ci		gcc_assert(TREE_CODE(field) == FIELD_DECL);
33762306a36Sopenharmony_ci		newtree[i] = field;
33862306a36Sopenharmony_ci	}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * enforce that we don't randomize the layout of the last
34262306a36Sopenharmony_ci	 * element of a struct if it's a proper flexible array
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	if (is_flexible_array(newtree[num_fields - 1])) {
34562306a36Sopenharmony_ci		has_flexarray = true;
34662306a36Sopenharmony_ci		shuffle_length--;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	shuffle(type, (tree *)newtree, shuffle_length);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * set up a bogus anonymous struct field designed to error out on unnamed struct initializers
35362306a36Sopenharmony_ci	 * as gcc provides no other way to detect such code
35462306a36Sopenharmony_ci	 */
35562306a36Sopenharmony_ci	list = make_node(FIELD_DECL);
35662306a36Sopenharmony_ci	TREE_CHAIN(list) = newtree[0];
35762306a36Sopenharmony_ci	TREE_TYPE(list) = void_type_node;
35862306a36Sopenharmony_ci	DECL_SIZE(list) = bitsize_zero_node;
35962306a36Sopenharmony_ci	DECL_NONADDRESSABLE_P(list) = 1;
36062306a36Sopenharmony_ci	DECL_FIELD_BIT_OFFSET(list) = bitsize_zero_node;
36162306a36Sopenharmony_ci	DECL_SIZE_UNIT(list) = size_zero_node;
36262306a36Sopenharmony_ci	DECL_FIELD_OFFSET(list) = size_zero_node;
36362306a36Sopenharmony_ci	DECL_CONTEXT(list) = type;
36462306a36Sopenharmony_ci	// to satisfy the constify plugin
36562306a36Sopenharmony_ci	TREE_READONLY(list) = 1;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	for (i = 0; i < num_fields - 1; i++)
36862306a36Sopenharmony_ci		TREE_CHAIN(newtree[i]) = newtree[i+1];
36962306a36Sopenharmony_ci	TREE_CHAIN(newtree[num_fields - 1]) = NULL_TREE;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	main_variant = TYPE_MAIN_VARIANT(type);
37262306a36Sopenharmony_ci	for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant)) {
37362306a36Sopenharmony_ci		TYPE_FIELDS(variant) = list;
37462306a36Sopenharmony_ci		TYPE_ATTRIBUTES(variant) = copy_list(TYPE_ATTRIBUTES(variant));
37562306a36Sopenharmony_ci		TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("randomize_performed"), NULL_TREE, TYPE_ATTRIBUTES(variant));
37662306a36Sopenharmony_ci		TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("designated_init"), NULL_TREE, TYPE_ATTRIBUTES(variant));
37762306a36Sopenharmony_ci		if (has_flexarray)
37862306a36Sopenharmony_ci			TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("has_flexarray"), NULL_TREE, TYPE_ATTRIBUTES(type));
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/*
38262306a36Sopenharmony_ci	 * force a re-layout of the main variant
38362306a36Sopenharmony_ci	 * the TYPE_SIZE for all variants will be recomputed
38462306a36Sopenharmony_ci	 * by finalize_type_size()
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci	TYPE_SIZE(main_variant) = NULL_TREE;
38762306a36Sopenharmony_ci	layout_type(main_variant);
38862306a36Sopenharmony_ci	gcc_assert(TYPE_SIZE(main_variant) != NULL_TREE);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 1;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/* from constify plugin */
39462306a36Sopenharmony_cistatic const_tree get_field_type(const_tree field)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	return strip_array_types(TREE_TYPE(field));
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/* from constify plugin */
40062306a36Sopenharmony_cistatic bool is_fptr(const_tree fieldtype)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	if (TREE_CODE(fieldtype) != POINTER_TYPE)
40362306a36Sopenharmony_ci		return false;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return TREE_CODE(TREE_TYPE(fieldtype)) == FUNCTION_TYPE;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci/* derived from constify plugin */
40962306a36Sopenharmony_cistatic int is_pure_ops_struct(const_tree node)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	const_tree field;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	gcc_assert(TREE_CODE(node) == RECORD_TYPE || TREE_CODE(node) == UNION_TYPE);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	for (field = TYPE_FIELDS(node); field; field = TREE_CHAIN(field)) {
41662306a36Sopenharmony_ci		const_tree fieldtype = get_field_type(field);
41762306a36Sopenharmony_ci		enum tree_code code = TREE_CODE(fieldtype);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (node == fieldtype)
42062306a36Sopenharmony_ci			continue;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		if (code == RECORD_TYPE || code == UNION_TYPE) {
42362306a36Sopenharmony_ci			if (!is_pure_ops_struct(fieldtype))
42462306a36Sopenharmony_ci				return 0;
42562306a36Sopenharmony_ci			continue;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		if (!is_fptr(fieldtype))
42962306a36Sopenharmony_ci			return 0;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 1;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic void randomize_type(tree type)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	tree variant;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	gcc_assert(TREE_CODE(type) == RECORD_TYPE);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
44262306a36Sopenharmony_ci		return;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (lookup_attribute("randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))) || is_pure_ops_struct(type))
44562306a36Sopenharmony_ci		relayout_struct(type);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	for (variant = TYPE_MAIN_VARIANT(type); variant; variant = TYPE_NEXT_VARIANT(variant)) {
44862306a36Sopenharmony_ci		TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
44962306a36Sopenharmony_ci		TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("randomize_considered"), NULL_TREE, TYPE_ATTRIBUTES(type));
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci#ifdef __DEBUG_PLUGIN
45262306a36Sopenharmony_ci	fprintf(stderr, "Marking randomize_considered on struct %s\n", ORIG_TYPE_NAME(type));
45362306a36Sopenharmony_ci#ifdef __DEBUG_VERBOSE
45462306a36Sopenharmony_ci	debug_tree(type);
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci#endif
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic void update_decl_size(tree decl)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	tree lastval, lastidx, field, init, type, flexsize;
46262306a36Sopenharmony_ci	unsigned HOST_WIDE_INT len;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	type = TREE_TYPE(decl);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (!lookup_attribute("has_flexarray", TYPE_ATTRIBUTES(type)))
46762306a36Sopenharmony_ci		return;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	init = DECL_INITIAL(decl);
47062306a36Sopenharmony_ci	if (init == NULL_TREE || init == error_mark_node)
47162306a36Sopenharmony_ci		return;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (TREE_CODE(init) != CONSTRUCTOR)
47462306a36Sopenharmony_ci		return;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	len = CONSTRUCTOR_NELTS(init);
47762306a36Sopenharmony_ci        if (!len)
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	lastval = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->value;
48162306a36Sopenharmony_ci	lastidx = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->index;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	for (field = TYPE_FIELDS(TREE_TYPE(decl)); TREE_CHAIN(field); field = TREE_CHAIN(field))
48462306a36Sopenharmony_ci		;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (lastidx != field)
48762306a36Sopenharmony_ci		return;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (TREE_CODE(lastval) != STRING_CST) {
49062306a36Sopenharmony_ci		error("Only string constants are supported as initializers "
49162306a36Sopenharmony_ci		      "for randomized structures with flexible arrays");
49262306a36Sopenharmony_ci		return;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	flexsize = bitsize_int(TREE_STRING_LENGTH(lastval) *
49662306a36Sopenharmony_ci		tree_to_uhwi(TYPE_SIZE(TREE_TYPE(TREE_TYPE(lastval)))));
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	DECL_SIZE(decl) = size_binop(PLUS_EXPR, TYPE_SIZE(type), flexsize);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	return;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void randomize_layout_finish_decl(void *event_data, void *data)
50562306a36Sopenharmony_ci{
50662306a36Sopenharmony_ci	tree decl = (tree)event_data;
50762306a36Sopenharmony_ci	tree type;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (decl == NULL_TREE || decl == error_mark_node)
51062306a36Sopenharmony_ci		return;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	type = TREE_TYPE(decl);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (TREE_CODE(decl) != VAR_DECL)
51562306a36Sopenharmony_ci		return;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
51862306a36Sopenharmony_ci		return;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)))
52162306a36Sopenharmony_ci		return;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	DECL_SIZE(decl) = 0;
52462306a36Sopenharmony_ci	DECL_SIZE_UNIT(decl) = 0;
52562306a36Sopenharmony_ci	SET_DECL_ALIGN(decl, 0);
52662306a36Sopenharmony_ci	SET_DECL_MODE (decl, VOIDmode);
52762306a36Sopenharmony_ci	SET_DECL_RTL(decl, 0);
52862306a36Sopenharmony_ci	update_decl_size(decl);
52962306a36Sopenharmony_ci	layout_decl(decl, 0);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void finish_type(void *event_data, void *data)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	tree type = (tree)event_data;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (type == NULL_TREE || type == error_mark_node)
53762306a36Sopenharmony_ci		return;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (TREE_CODE(type) != RECORD_TYPE)
54062306a36Sopenharmony_ci		return;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (TYPE_FIELDS(type) == NULL_TREE)
54362306a36Sopenharmony_ci		return;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
54662306a36Sopenharmony_ci		return;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci#ifdef __DEBUG_PLUGIN
54962306a36Sopenharmony_ci	fprintf(stderr, "Calling randomize_type on %s\n", ORIG_TYPE_NAME(type));
55062306a36Sopenharmony_ci#endif
55162306a36Sopenharmony_ci#ifdef __DEBUG_VERBOSE
55262306a36Sopenharmony_ci	debug_tree(type);
55362306a36Sopenharmony_ci#endif
55462306a36Sopenharmony_ci	randomize_type(type);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic struct attribute_spec randomize_layout_attr = { };
56062306a36Sopenharmony_cistatic struct attribute_spec no_randomize_layout_attr = { };
56162306a36Sopenharmony_cistatic struct attribute_spec randomize_considered_attr = { };
56262306a36Sopenharmony_cistatic struct attribute_spec randomize_performed_attr = { };
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic void register_attributes(void *event_data, void *data)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	randomize_layout_attr.name		= "randomize_layout";
56762306a36Sopenharmony_ci	randomize_layout_attr.type_required	= true;
56862306a36Sopenharmony_ci	randomize_layout_attr.handler		= handle_randomize_layout_attr;
56962306a36Sopenharmony_ci	randomize_layout_attr.affects_type_identity = true;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	no_randomize_layout_attr.name		= "no_randomize_layout";
57262306a36Sopenharmony_ci	no_randomize_layout_attr.type_required	= true;
57362306a36Sopenharmony_ci	no_randomize_layout_attr.handler	= handle_randomize_layout_attr;
57462306a36Sopenharmony_ci	no_randomize_layout_attr.affects_type_identity = true;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	randomize_considered_attr.name		= "randomize_considered";
57762306a36Sopenharmony_ci	randomize_considered_attr.type_required	= true;
57862306a36Sopenharmony_ci	randomize_considered_attr.handler	= handle_randomize_considered_attr;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	randomize_performed_attr.name		= "randomize_performed";
58162306a36Sopenharmony_ci	randomize_performed_attr.type_required	= true;
58262306a36Sopenharmony_ci	randomize_performed_attr.handler	= handle_randomize_performed_attr;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	register_attribute(&randomize_layout_attr);
58562306a36Sopenharmony_ci	register_attribute(&no_randomize_layout_attr);
58662306a36Sopenharmony_ci	register_attribute(&randomize_considered_attr);
58762306a36Sopenharmony_ci	register_attribute(&randomize_performed_attr);
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic void check_bad_casts_in_constructor(tree var, tree init)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	unsigned HOST_WIDE_INT idx;
59362306a36Sopenharmony_ci	tree field, val;
59462306a36Sopenharmony_ci	tree field_type, val_type;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	FOR_EACH_CONSTRUCTOR_ELT(CONSTRUCTOR_ELTS(init), idx, field, val) {
59762306a36Sopenharmony_ci		if (TREE_CODE(val) == CONSTRUCTOR) {
59862306a36Sopenharmony_ci			check_bad_casts_in_constructor(var, val);
59962306a36Sopenharmony_ci			continue;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		/* pipacs' plugin creates franken-arrays that differ from those produced by
60362306a36Sopenharmony_ci		   normal code which all have valid 'field' trees. work around this */
60462306a36Sopenharmony_ci		if (field == NULL_TREE)
60562306a36Sopenharmony_ci			continue;
60662306a36Sopenharmony_ci		field_type = TREE_TYPE(field);
60762306a36Sopenharmony_ci		val_type = TREE_TYPE(val);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		if (TREE_CODE(field_type) != POINTER_TYPE || TREE_CODE(val_type) != POINTER_TYPE)
61062306a36Sopenharmony_ci			continue;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci		if (field_type == val_type)
61362306a36Sopenharmony_ci			continue;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci		field_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(field_type))));
61662306a36Sopenharmony_ci		val_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(val_type))));
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		if (field_type == void_type_node)
61962306a36Sopenharmony_ci			continue;
62062306a36Sopenharmony_ci		if (field_type == val_type)
62162306a36Sopenharmony_ci			continue;
62262306a36Sopenharmony_ci		if (TREE_CODE(val_type) != RECORD_TYPE)
62362306a36Sopenharmony_ci			continue;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(val_type)))
62662306a36Sopenharmony_ci			continue;
62762306a36Sopenharmony_ci		MISMATCH(DECL_SOURCE_LOCATION(var), "constructor\n", TYPE_MAIN_VARIANT(field_type), TYPE_MAIN_VARIANT(val_type));
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/* derived from the constify plugin */
63262306a36Sopenharmony_cistatic void check_global_variables(void *event_data, void *data)
63362306a36Sopenharmony_ci{
63462306a36Sopenharmony_ci	struct varpool_node *node;
63562306a36Sopenharmony_ci	tree init;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	FOR_EACH_VARIABLE(node) {
63862306a36Sopenharmony_ci		tree var = NODE_DECL(node);
63962306a36Sopenharmony_ci		init = DECL_INITIAL(var);
64062306a36Sopenharmony_ci		if (init == NULL_TREE)
64162306a36Sopenharmony_ci			continue;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		if (TREE_CODE(init) != CONSTRUCTOR)
64462306a36Sopenharmony_ci			continue;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci		check_bad_casts_in_constructor(var, init);
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_cistatic bool dominated_by_is_err(const_tree rhs, basic_block bb)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	basic_block dom;
65362306a36Sopenharmony_ci	gimple dom_stmt;
65462306a36Sopenharmony_ci	gimple call_stmt;
65562306a36Sopenharmony_ci	const_tree dom_lhs;
65662306a36Sopenharmony_ci	const_tree poss_is_err_cond;
65762306a36Sopenharmony_ci	const_tree poss_is_err_func;
65862306a36Sopenharmony_ci	const_tree is_err_arg;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	dom = get_immediate_dominator(CDI_DOMINATORS, bb);
66162306a36Sopenharmony_ci	if (!dom)
66262306a36Sopenharmony_ci		return false;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	dom_stmt = last_stmt(dom);
66562306a36Sopenharmony_ci	if (!dom_stmt)
66662306a36Sopenharmony_ci		return false;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (gimple_code(dom_stmt) != GIMPLE_COND)
66962306a36Sopenharmony_ci		return false;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	if (gimple_cond_code(dom_stmt) != NE_EXPR)
67262306a36Sopenharmony_ci		return false;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (!integer_zerop(gimple_cond_rhs(dom_stmt)))
67562306a36Sopenharmony_ci		return false;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	poss_is_err_cond = gimple_cond_lhs(dom_stmt);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	if (TREE_CODE(poss_is_err_cond) != SSA_NAME)
68062306a36Sopenharmony_ci		return false;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	call_stmt = SSA_NAME_DEF_STMT(poss_is_err_cond);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (gimple_code(call_stmt) != GIMPLE_CALL)
68562306a36Sopenharmony_ci		return false;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	dom_lhs = gimple_get_lhs(call_stmt);
68862306a36Sopenharmony_ci	poss_is_err_func = gimple_call_fndecl(call_stmt);
68962306a36Sopenharmony_ci	if (!poss_is_err_func)
69062306a36Sopenharmony_ci		return false;
69162306a36Sopenharmony_ci	if (dom_lhs != poss_is_err_cond)
69262306a36Sopenharmony_ci		return false;
69362306a36Sopenharmony_ci	if (strcmp(DECL_NAME_POINTER(poss_is_err_func), "IS_ERR"))
69462306a36Sopenharmony_ci		return false;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	is_err_arg = gimple_call_arg(call_stmt, 0);
69762306a36Sopenharmony_ci	if (!is_err_arg)
69862306a36Sopenharmony_ci		return false;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (is_err_arg != rhs)
70162306a36Sopenharmony_ci		return false;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return true;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void handle_local_var_initializers(void)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	tree var;
70962306a36Sopenharmony_ci	unsigned int i;
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	FOR_EACH_LOCAL_DECL(cfun, i, var) {
71262306a36Sopenharmony_ci		tree init = DECL_INITIAL(var);
71362306a36Sopenharmony_ci		if (!init)
71462306a36Sopenharmony_ci			continue;
71562306a36Sopenharmony_ci		if (TREE_CODE(init) != CONSTRUCTOR)
71662306a36Sopenharmony_ci			continue;
71762306a36Sopenharmony_ci		check_bad_casts_in_constructor(var, init);
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci/*
72262306a36Sopenharmony_ci * iterate over all statements to find "bad" casts:
72362306a36Sopenharmony_ci * those where the address of the start of a structure is cast
72462306a36Sopenharmony_ci * to a pointer of a structure of a different type, or a
72562306a36Sopenharmony_ci * structure pointer type is cast to a different structure pointer type
72662306a36Sopenharmony_ci */
72762306a36Sopenharmony_cistatic unsigned int find_bad_casts_execute(void)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	basic_block bb;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	handle_local_var_initializers();
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	FOR_EACH_BB_FN(bb, cfun) {
73462306a36Sopenharmony_ci		gimple_stmt_iterator gsi;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci		for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
73762306a36Sopenharmony_ci			gimple stmt;
73862306a36Sopenharmony_ci			const_tree lhs;
73962306a36Sopenharmony_ci			const_tree lhs_type;
74062306a36Sopenharmony_ci			const_tree rhs1;
74162306a36Sopenharmony_ci			const_tree rhs_type;
74262306a36Sopenharmony_ci			const_tree ptr_lhs_type;
74362306a36Sopenharmony_ci			const_tree ptr_rhs_type;
74462306a36Sopenharmony_ci			const_tree op0;
74562306a36Sopenharmony_ci			const_tree op0_type;
74662306a36Sopenharmony_ci			enum tree_code rhs_code;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci			stmt = gsi_stmt(gsi);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci#ifdef __DEBUG_PLUGIN
75162306a36Sopenharmony_ci#ifdef __DEBUG_VERBOSE
75262306a36Sopenharmony_ci			debug_gimple_stmt(stmt);
75362306a36Sopenharmony_ci			debug_tree(gimple_get_lhs(stmt));
75462306a36Sopenharmony_ci#endif
75562306a36Sopenharmony_ci#endif
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci			if (gimple_code(stmt) != GIMPLE_ASSIGN)
75862306a36Sopenharmony_ci				continue;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci#ifdef __DEBUG_PLUGIN
76162306a36Sopenharmony_ci#ifdef __DEBUG_VERBOSE
76262306a36Sopenharmony_ci			debug_tree(gimple_assign_rhs1(stmt));
76362306a36Sopenharmony_ci#endif
76462306a36Sopenharmony_ci#endif
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci			rhs_code = gimple_assign_rhs_code(stmt);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci			if (rhs_code != ADDR_EXPR && rhs_code != SSA_NAME)
77062306a36Sopenharmony_ci				continue;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci			lhs = gimple_get_lhs(stmt);
77362306a36Sopenharmony_ci			lhs_type = TREE_TYPE(lhs);
77462306a36Sopenharmony_ci			rhs1 = gimple_assign_rhs1(stmt);
77562306a36Sopenharmony_ci			rhs_type = TREE_TYPE(rhs1);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci			if (TREE_CODE(rhs_type) != POINTER_TYPE ||
77862306a36Sopenharmony_ci			    TREE_CODE(lhs_type) != POINTER_TYPE)
77962306a36Sopenharmony_ci				continue;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci			ptr_lhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(lhs_type))));
78262306a36Sopenharmony_ci			ptr_rhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(rhs_type))));
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci			if (ptr_rhs_type == void_type_node)
78562306a36Sopenharmony_ci				continue;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci			if (ptr_lhs_type == void_type_node)
78862306a36Sopenharmony_ci				continue;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci			if (dominated_by_is_err(rhs1, bb))
79162306a36Sopenharmony_ci				continue;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci			if (TREE_CODE(ptr_rhs_type) != RECORD_TYPE) {
79462306a36Sopenharmony_ci#ifndef __DEBUG_PLUGIN
79562306a36Sopenharmony_ci				if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_lhs_type)))
79662306a36Sopenharmony_ci#endif
79762306a36Sopenharmony_ci				MISMATCH(gimple_location(stmt), "rhs", ptr_lhs_type, ptr_rhs_type);
79862306a36Sopenharmony_ci				continue;
79962306a36Sopenharmony_ci			}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci			if (rhs_code == SSA_NAME && ptr_lhs_type == ptr_rhs_type)
80262306a36Sopenharmony_ci				continue;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci			if (rhs_code == ADDR_EXPR) {
80562306a36Sopenharmony_ci				op0 = TREE_OPERAND(rhs1, 0);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci				if (op0 == NULL_TREE)
80862306a36Sopenharmony_ci					continue;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci				if (TREE_CODE(op0) != VAR_DECL)
81162306a36Sopenharmony_ci					continue;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci				op0_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(op0))));
81462306a36Sopenharmony_ci				if (op0_type == ptr_lhs_type)
81562306a36Sopenharmony_ci					continue;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci#ifndef __DEBUG_PLUGIN
81862306a36Sopenharmony_ci				if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(op0_type)))
81962306a36Sopenharmony_ci#endif
82062306a36Sopenharmony_ci				MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
82162306a36Sopenharmony_ci			} else {
82262306a36Sopenharmony_ci				const_tree ssa_name_var = SSA_NAME_VAR(rhs1);
82362306a36Sopenharmony_ci				/* skip bogus type casts introduced by container_of */
82462306a36Sopenharmony_ci				if (ssa_name_var != NULL_TREE && DECL_NAME(ssa_name_var) &&
82562306a36Sopenharmony_ci				    !strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr"))
82662306a36Sopenharmony_ci					continue;
82762306a36Sopenharmony_ci#ifndef __DEBUG_PLUGIN
82862306a36Sopenharmony_ci				if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type)))
82962306a36Sopenharmony_ci#endif
83062306a36Sopenharmony_ci				MISMATCH(gimple_location(stmt), "ssa", ptr_lhs_type, ptr_rhs_type);
83162306a36Sopenharmony_ci			}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci		}
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci	return 0;
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci#define PASS_NAME find_bad_casts
83962306a36Sopenharmony_ci#define NO_GATE
84062306a36Sopenharmony_ci#define TODO_FLAGS_FINISH TODO_dump_func
84162306a36Sopenharmony_ci#include "gcc-generate-gimple-pass.h"
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	int i;
84662306a36Sopenharmony_ci	const char * const plugin_name = plugin_info->base_name;
84762306a36Sopenharmony_ci	const int argc = plugin_info->argc;
84862306a36Sopenharmony_ci	const struct plugin_argument * const argv = plugin_info->argv;
84962306a36Sopenharmony_ci	bool enable = true;
85062306a36Sopenharmony_ci	int obtained_seed = 0;
85162306a36Sopenharmony_ci	struct register_pass_info find_bad_casts_pass_info;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	find_bad_casts_pass_info.pass			= make_find_bad_casts_pass();
85462306a36Sopenharmony_ci	find_bad_casts_pass_info.reference_pass_name	= "ssa";
85562306a36Sopenharmony_ci	find_bad_casts_pass_info.ref_pass_instance_number	= 1;
85662306a36Sopenharmony_ci	find_bad_casts_pass_info.pos_op			= PASS_POS_INSERT_AFTER;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (!plugin_default_version_check(version, &gcc_version)) {
85962306a36Sopenharmony_ci		error(G_("incompatible gcc/plugin versions"));
86062306a36Sopenharmony_ci		return 1;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
86462306a36Sopenharmony_ci		inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
86562306a36Sopenharmony_ci		enable = false;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	for (i = 0; i < argc; ++i) {
86962306a36Sopenharmony_ci		if (!strcmp(argv[i].key, "disable")) {
87062306a36Sopenharmony_ci			enable = false;
87162306a36Sopenharmony_ci			continue;
87262306a36Sopenharmony_ci		}
87362306a36Sopenharmony_ci		if (!strcmp(argv[i].key, "performance-mode")) {
87462306a36Sopenharmony_ci			performance_mode = 1;
87562306a36Sopenharmony_ci			continue;
87662306a36Sopenharmony_ci		}
87762306a36Sopenharmony_ci		error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
87862306a36Sopenharmony_ci	}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (strlen(randstruct_seed) != 64) {
88162306a36Sopenharmony_ci		error(G_("invalid seed value supplied for %s plugin"), plugin_name);
88262306a36Sopenharmony_ci		return 1;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci	obtained_seed = sscanf(randstruct_seed, "%016llx%016llx%016llx%016llx",
88562306a36Sopenharmony_ci		&shuffle_seed[0], &shuffle_seed[1], &shuffle_seed[2], &shuffle_seed[3]);
88662306a36Sopenharmony_ci	if (obtained_seed != 4) {
88762306a36Sopenharmony_ci		error(G_("Invalid seed supplied for %s plugin"), plugin_name);
88862306a36Sopenharmony_ci		return 1;
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	register_callback(plugin_name, PLUGIN_INFO, NULL, &randomize_layout_plugin_info);
89262306a36Sopenharmony_ci	if (enable) {
89362306a36Sopenharmony_ci		register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, check_global_variables, NULL);
89462306a36Sopenharmony_ci		register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &find_bad_casts_pass_info);
89562306a36Sopenharmony_ci		register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
89662306a36Sopenharmony_ci		register_callback(plugin_name, PLUGIN_FINISH_DECL, randomize_layout_finish_decl, NULL);
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci	register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
902