aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/gcc-plugins
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2017-05-06 02:37:45 -0400
committerKees Cook <keescook@chromium.org>2017-06-22 19:15:45 -0400
commit313dd1b629219db50cad532dba6a3b3b22ffe622 (patch)
treef365980765f5f1f556eb4800e542686677179e9d /scripts/gcc-plugins
parent0aa5e49c6845ecd82531341085f367767c9f419a (diff)
gcc-plugins: Add the randstruct plugin
This randstruct plugin is modified from Brad Spengler/PaX Team's code in the last public patch of grsecurity/PaX based on my understanding of the code. Changes or omissions from the original code are mine and don't reflect the original grsecurity/PaX code. The randstruct GCC plugin randomizes the layout of selected structures at compile time, as a probabilistic defense against attacks that need to know the layout of structures within the kernel. This is most useful for "in-house" kernel builds where neither the randomization seed nor other build artifacts are made available to an attacker. While less useful for distribution kernels (where the randomization seed must be exposed for third party kernel module builds), it still has some value there since now all kernel builds would need to be tracked by an attacker. In more performance sensitive scenarios, GCC_PLUGIN_RANDSTRUCT_PERFORMANCE can be selected to make a best effort to restrict randomization to cacheline-sized groups of elements, and will not randomize bitfields. This comes at the cost of reduced randomization. Two annotations are defined,__randomize_layout and __no_randomize_layout, which respectively tell the plugin to either randomize or not to randomize instances of the struct in question. Follow-on patches enable the auto-detection logic for selecting structures for randomization that contain only function pointers. It is disabled here to assist with bisection. Since any randomized structs must be initialized using designated initializers, __randomize_layout includes the __designated_init annotation even when the plugin is disabled so that all builds will require the needed initialization. (With the plugin enabled, annotations for automatically chosen structures are marked as well.) The main differences between this implemenation and grsecurity are: - disable automatic struct selection (to be enabled in follow-up patch) - add designated_init attribute at runtime and for manual marking - clarify debugging output to differentiate bad cast warnings - add whitelisting infrastructure - support gcc 7's DECL_ALIGN and DECL_MODE changes (Laura Abbott) - raise minimum required GCC version to 4.7 Earlier versions of this patch series were ported by Michael Leibowitz. Signed-off-by: Kees Cook <keescook@chromium.org>
Diffstat (limited to 'scripts/gcc-plugins')
-rw-r--r--scripts/gcc-plugins/.gitignore1
-rw-r--r--scripts/gcc-plugins/Makefile8
-rw-r--r--scripts/gcc-plugins/gcc-common.h5
-rw-r--r--scripts/gcc-plugins/gen-random-seed.sh8
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c1020
5 files changed, 1042 insertions, 0 deletions
diff --git a/scripts/gcc-plugins/.gitignore b/scripts/gcc-plugins/.gitignore
new file mode 100644
index 000000000000..de92ed9e3d83
--- /dev/null
+++ b/scripts/gcc-plugins/.gitignore
@@ -0,0 +1 @@
randomize_layout_seed.h
diff --git a/scripts/gcc-plugins/Makefile b/scripts/gcc-plugins/Makefile
index 8b29dc17c73c..214eb2335c31 100644
--- a/scripts/gcc-plugins/Makefile
+++ b/scripts/gcc-plugins/Makefile
@@ -18,6 +18,14 @@ endif
18 18
19export HOSTLIBS 19export HOSTLIBS
20 20
21$(obj)/randomize_layout_plugin.o: $(objtree)/$(obj)/randomize_layout_seed.h
22quiet_cmd_create_randomize_layout_seed = GENSEED $@
23cmd_create_randomize_layout_seed = \
24 $(CONFIG_SHELL) $(srctree)/$(src)/gen-random-seed.sh $@ $(objtree)/include/generated/randomize_layout_hash.h
25$(objtree)/$(obj)/randomize_layout_seed.h: FORCE
26 $(call if_changed,create_randomize_layout_seed)
27targets = randomize_layout_seed.h randomize_layout_hash.h
28
21$(HOSTLIBS)-y := $(foreach p,$(GCC_PLUGIN),$(if $(findstring /,$(p)),,$(p))) 29$(HOSTLIBS)-y := $(foreach p,$(GCC_PLUGIN),$(if $(findstring /,$(p)),,$(p)))
22always := $($(HOSTLIBS)-y) 30always := $($(HOSTLIBS)-y)
23 31
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 918953ca4527..6948898b3cdf 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -953,4 +953,9 @@ static inline void debug_gimple_stmt(const_gimple s)
953 get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep) 953 get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep)
954#endif 954#endif
955 955
956#if BUILDING_GCC_VERSION < 7000
957#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align)
958#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode)
959#endif
960
956#endif 961#endif
diff --git a/scripts/gcc-plugins/gen-random-seed.sh b/scripts/gcc-plugins/gen-random-seed.sh
new file mode 100644
index 000000000000..7514850f4815
--- /dev/null
+++ b/scripts/gcc-plugins/gen-random-seed.sh
@@ -0,0 +1,8 @@
1#!/bin/sh
2
3if [ ! -f "$1" ]; then
4 SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'`
5 echo "const char *randstruct_seed = \"$SEED\";" > "$1"
6 HASH=`echo -n "$SEED" | sha256sum | cut -d" " -f1 | tr -d ' \n'`
7 echo "#define RANDSTRUCT_HASHED_SEED \"$HASH\"" > "$2"
8fi
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
new file mode 100644
index 000000000000..e1d1ba28739f
--- /dev/null
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -0,0 +1,1020 @@
1/*
2 * Copyright 2014-2016 by Open Source Security, Inc., Brad Spengler <spender@grsecurity.net>
3 * and PaX Team <pageexec@freemail.hu>
4 * Licensed under the GPL v2
5 *
6 * Note: the choice of the license means that the compilation process is
7 * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
8 * but for the kernel it doesn't matter since it doesn't link against
9 * any of the gcc libraries
10 *
11 * Usage:
12 * $ # for 4.5/4.6/C based 4.7
13 * $ 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
14 * $ # for C++ based 4.7/4.8+
15 * $ 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
16 * $ gcc -fplugin=./randomize_layout_plugin.so test.c -O2
17 */
18
19#include "gcc-common.h"
20#include "randomize_layout_seed.h"
21
22#if BUILDING_GCC_MAJOR < 4 || (BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR < 7)
23#error "The RANDSTRUCT plugin requires GCC 4.7 or newer."
24#endif
25
26#define ORIG_TYPE_NAME(node) \
27 (TYPE_NAME(TYPE_MAIN_VARIANT(node)) != NULL_TREE ? ((const unsigned char *)IDENTIFIER_POINTER(TYPE_NAME(TYPE_MAIN_VARIANT(node)))) : (const unsigned char *)"anonymous")
28
29#define INFORM(loc, msg, ...) inform(loc, "randstruct: " msg, ##__VA_ARGS__)
30#define MISMATCH(loc, how, ...) INFORM(loc, "casting between randomized structure pointer types (" how "): %qT and %qT\n", __VA_ARGS__)
31
32__visible int plugin_is_GPL_compatible;
33
34static int performance_mode;
35
36static struct plugin_info randomize_layout_plugin_info = {
37 .version = "201402201816vanilla",
38 .help = "disable\t\t\tdo not activate plugin\n"
39 "performance-mode\tenable cacheline-aware layout randomization\n"
40};
41
42struct whitelist_entry {
43 const char *pathname;
44 const char *lhs;
45 const char *rhs;
46};
47
48static const struct whitelist_entry whitelist[] = {
49 { }
50};
51
52/* from old Linux dcache.h */
53static inline unsigned long
54partial_name_hash(unsigned long c, unsigned long prevhash)
55{
56 return (prevhash + (c << 4) + (c >> 4)) * 11;
57}
58static inline unsigned int
59name_hash(const unsigned char *name)
60{
61 unsigned long hash = 0;
62 unsigned int len = strlen((const char *)name);
63 while (len--)
64 hash = partial_name_hash(*name++, hash);
65 return (unsigned int)hash;
66}
67
68static tree handle_randomize_layout_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
69{
70 tree type;
71
72 *no_add_attrs = true;
73 if (TREE_CODE(*node) == FUNCTION_DECL) {
74 error("%qE attribute does not apply to functions (%qF)", name, *node);
75 return NULL_TREE;
76 }
77
78 if (TREE_CODE(*node) == PARM_DECL) {
79 error("%qE attribute does not apply to function parameters (%qD)", name, *node);
80 return NULL_TREE;
81 }
82
83 if (TREE_CODE(*node) == VAR_DECL) {
84 error("%qE attribute does not apply to variables (%qD)", name, *node);
85 return NULL_TREE;
86 }
87
88 if (TYPE_P(*node)) {
89 type = *node;
90 } else {
91 gcc_assert(TREE_CODE(*node) == TYPE_DECL);
92 type = TREE_TYPE(*node);
93 }
94
95 if (TREE_CODE(type) != RECORD_TYPE) {
96 error("%qE attribute used on %qT applies to struct types only", name, type);
97 return NULL_TREE;
98 }
99
100 if (lookup_attribute(IDENTIFIER_POINTER(name), TYPE_ATTRIBUTES(type))) {
101 error("%qE attribute is already applied to the type %qT", name, type);
102 return NULL_TREE;
103 }
104
105 *no_add_attrs = false;
106
107 return NULL_TREE;
108}
109
110/* set on complete types that we don't need to inspect further at all */
111static tree handle_randomize_considered_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
112{
113 *no_add_attrs = false;
114 return NULL_TREE;
115}
116
117/*
118 * set on types that we've performed a shuffle on, to prevent re-shuffling
119 * this does not preclude us from inspecting its fields for potential shuffles
120 */
121static tree handle_randomize_performed_attr(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
122{
123 *no_add_attrs = false;
124 return NULL_TREE;
125}
126
127/*
128 * 64bit variant of Bob Jenkins' public domain PRNG
129 * 256 bits of internal state
130 */
131
132typedef unsigned long long u64;
133
134typedef struct ranctx { u64 a; u64 b; u64 c; u64 d; } ranctx;
135
136#define rot(x,k) (((x)<<(k))|((x)>>(64-(k))))
137static u64 ranval(ranctx *x) {
138 u64 e = x->a - rot(x->b, 7);
139 x->a = x->b ^ rot(x->c, 13);
140 x->b = x->c + rot(x->d, 37);
141 x->c = x->d + e;
142 x->d = e + x->a;
143 return x->d;
144}
145
146static void raninit(ranctx *x, u64 *seed) {
147 int i;
148
149 x->a = seed[0];
150 x->b = seed[1];
151 x->c = seed[2];
152 x->d = seed[3];
153
154 for (i=0; i < 30; ++i)
155 (void)ranval(x);
156}
157
158static u64 shuffle_seed[4];
159
160struct partition_group {
161 tree tree_start;
162 unsigned long start;
163 unsigned long length;
164};
165
166static void partition_struct(tree *fields, unsigned long length, struct partition_group *size_groups, unsigned long *num_groups)
167{
168 unsigned long i;
169 unsigned long accum_size = 0;
170 unsigned long accum_length = 0;
171 unsigned long group_idx = 0;
172
173 gcc_assert(length < INT_MAX);
174
175 memset(size_groups, 0, sizeof(struct partition_group) * length);
176
177 for (i = 0; i < length; i++) {
178 if (size_groups[group_idx].tree_start == NULL_TREE) {
179 size_groups[group_idx].tree_start = fields[i];
180 size_groups[group_idx].start = i;
181 accum_length = 0;
182 accum_size = 0;
183 }
184 accum_size += (unsigned long)int_size_in_bytes(TREE_TYPE(fields[i]));
185 accum_length++;
186 if (accum_size >= 64) {
187 size_groups[group_idx].length = accum_length;
188 accum_length = 0;
189 group_idx++;
190 }
191 }
192
193 if (size_groups[group_idx].tree_start != NULL_TREE &&
194 !size_groups[group_idx].length) {
195 size_groups[group_idx].length = accum_length;
196 group_idx++;
197 }
198
199 *num_groups = group_idx;
200}
201
202static void performance_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
203{
204 unsigned long i, x;
205 struct partition_group size_group[length];
206 unsigned long num_groups = 0;
207 unsigned long randnum;
208
209 partition_struct(newtree, length, (struct partition_group *)&size_group, &num_groups);
210 for (i = num_groups - 1; i > 0; i--) {
211 struct partition_group tmp;
212 randnum = ranval(prng_state) % (i + 1);
213 tmp = size_group[i];
214 size_group[i] = size_group[randnum];
215 size_group[randnum] = tmp;
216 }
217
218 for (x = 0; x < num_groups; x++) {
219 for (i = size_group[x].start + size_group[x].length - 1; i > size_group[x].start; i--) {
220 tree tmp;
221 if (DECL_BIT_FIELD_TYPE(newtree[i]))
222 continue;
223 randnum = ranval(prng_state) % (i + 1);
224 // we could handle this case differently if desired
225 if (DECL_BIT_FIELD_TYPE(newtree[randnum]))
226 continue;
227 tmp = newtree[i];
228 newtree[i] = newtree[randnum];
229 newtree[randnum] = tmp;
230 }
231 }
232}
233
234static void full_shuffle(tree *newtree, unsigned long length, ranctx *prng_state)
235{
236 unsigned long i, randnum;
237
238 for (i = length - 1; i > 0; i--) {
239 tree tmp;
240 randnum = ranval(prng_state) % (i + 1);
241 tmp = newtree[i];
242 newtree[i] = newtree[randnum];
243 newtree[randnum] = tmp;
244 }
245}
246
247/* modern in-place Fisher-Yates shuffle */
248static void shuffle(const_tree type, tree *newtree, unsigned long length)
249{
250 unsigned long i;
251 u64 seed[4];
252 ranctx prng_state;
253 const unsigned char *structname;
254
255 if (length == 0)
256 return;
257
258 gcc_assert(TREE_CODE(type) == RECORD_TYPE);
259
260 structname = ORIG_TYPE_NAME(type);
261
262#ifdef __DEBUG_PLUGIN
263 fprintf(stderr, "Shuffling struct %s %p\n", (const char *)structname, type);
264#ifdef __DEBUG_VERBOSE
265 debug_tree((tree)type);
266#endif
267#endif
268
269 for (i = 0; i < 4; i++) {
270 seed[i] = shuffle_seed[i];
271 seed[i] ^= name_hash(structname);
272 }
273
274 raninit(&prng_state, (u64 *)&seed);
275
276 if (performance_mode)
277 performance_shuffle(newtree, length, &prng_state);
278 else
279 full_shuffle(newtree, length, &prng_state);
280}
281
282static bool is_flexible_array(const_tree field)
283{
284 const_tree fieldtype;
285 const_tree typesize;
286 const_tree elemtype;
287 const_tree elemsize;
288
289 fieldtype = TREE_TYPE(field);
290 typesize = TYPE_SIZE(fieldtype);
291
292 if (TREE_CODE(fieldtype) != ARRAY_TYPE)
293 return false;
294
295 elemtype = TREE_TYPE(fieldtype);
296 elemsize = TYPE_SIZE(elemtype);
297
298 /* size of type is represented in bits */
299
300 if (typesize == NULL_TREE && TYPE_DOMAIN(fieldtype) != NULL_TREE &&
301 TYPE_MAX_VALUE(TYPE_DOMAIN(fieldtype)) == NULL_TREE)
302 return true;
303
304 if (typesize != NULL_TREE &&
305 (TREE_CONSTANT(typesize) && (!tree_to_uhwi(typesize) ||
306 tree_to_uhwi(typesize) == tree_to_uhwi(elemsize))))
307 return true;
308
309 return false;
310}
311
312static int relayout_struct(tree type)
313{
314 unsigned long num_fields = (unsigned long)list_length(TYPE_FIELDS(type));
315 unsigned long shuffle_length = num_fields;
316 tree field;
317 tree newtree[num_fields];
318 unsigned long i;
319 tree list;
320 tree variant;
321 tree main_variant;
322 expanded_location xloc;
323 bool has_flexarray = false;
324
325 if (TYPE_FIELDS(type) == NULL_TREE)
326 return 0;
327
328 if (num_fields < 2)
329 return 0;
330
331 gcc_assert(TREE_CODE(type) == RECORD_TYPE);
332
333 gcc_assert(num_fields < INT_MAX);
334
335 if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)) ||
336 lookup_attribute("no_randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))))
337 return 0;
338
339 /* Workaround for 3rd-party VirtualBox source that we can't modify ourselves */
340 if (!strcmp((const char *)ORIG_TYPE_NAME(type), "INTNETTRUNKFACTORY") ||
341 !strcmp((const char *)ORIG_TYPE_NAME(type), "RAWPCIFACTORY"))
342 return 0;
343
344 /* throw out any structs in uapi */
345 xloc = expand_location(DECL_SOURCE_LOCATION(TYPE_FIELDS(type)));
346
347 if (strstr(xloc.file, "/uapi/"))
348 error(G_("attempted to randomize userland API struct %s"), ORIG_TYPE_NAME(type));
349
350 for (field = TYPE_FIELDS(type), i = 0; field; field = TREE_CHAIN(field), i++) {
351 gcc_assert(TREE_CODE(field) == FIELD_DECL);
352 newtree[i] = field;
353 }
354
355 /*
356 * enforce that we don't randomize the layout of the last
357 * element of a struct if it's a 0 or 1-length array
358 * or a proper flexible array
359 */
360 if (is_flexible_array(newtree[num_fields - 1])) {
361 has_flexarray = true;
362 shuffle_length--;
363 }
364
365 shuffle(type, (tree *)newtree, shuffle_length);
366
367 /*
368 * set up a bogus anonymous struct field designed to error out on unnamed struct initializers
369 * as gcc provides no other way to detect such code
370 */
371 list = make_node(FIELD_DECL);
372 TREE_CHAIN(list) = newtree[0];
373 TREE_TYPE(list) = void_type_node;
374 DECL_SIZE(list) = bitsize_zero_node;
375 DECL_NONADDRESSABLE_P(list) = 1;
376 DECL_FIELD_BIT_OFFSET(list) = bitsize_zero_node;
377 DECL_SIZE_UNIT(list) = size_zero_node;
378 DECL_FIELD_OFFSET(list) = size_zero_node;
379 DECL_CONTEXT(list) = type;
380 // to satisfy the constify plugin
381 TREE_READONLY(list) = 1;
382
383 for (i = 0; i < num_fields - 1; i++)
384 TREE_CHAIN(newtree[i]) = newtree[i+1];
385 TREE_CHAIN(newtree[num_fields - 1]) = NULL_TREE;
386
387 main_variant = TYPE_MAIN_VARIANT(type);
388 for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant)) {
389 TYPE_FIELDS(variant) = list;
390 TYPE_ATTRIBUTES(variant) = copy_list(TYPE_ATTRIBUTES(variant));
391 TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("randomize_performed"), NULL_TREE, TYPE_ATTRIBUTES(variant));
392 TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("designated_init"), NULL_TREE, TYPE_ATTRIBUTES(variant));
393 if (has_flexarray)
394 TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("has_flexarray"), NULL_TREE, TYPE_ATTRIBUTES(type));
395 }
396
397 /*
398 * force a re-layout of the main variant
399 * the TYPE_SIZE for all variants will be recomputed
400 * by finalize_type_size()
401 */
402 TYPE_SIZE(main_variant) = NULL_TREE;
403 layout_type(main_variant);
404 gcc_assert(TYPE_SIZE(main_variant) != NULL_TREE);
405
406 return 1;
407}
408
409/* from constify plugin */
410static const_tree get_field_type(const_tree field)
411{
412 return strip_array_types(TREE_TYPE(field));
413}
414
415/* from constify plugin */
416static bool is_fptr(const_tree fieldtype)
417{
418 if (TREE_CODE(fieldtype) != POINTER_TYPE)
419 return false;
420
421 return TREE_CODE(TREE_TYPE(fieldtype)) == FUNCTION_TYPE;
422}
423
424/* derived from constify plugin */
425static int is_pure_ops_struct(const_tree node)
426{
427 const_tree field;
428
429 gcc_assert(TREE_CODE(node) == RECORD_TYPE || TREE_CODE(node) == UNION_TYPE);
430
431 /* XXX: Do not apply randomization to all-ftpr structs yet. */
432 return 0;
433
434 for (field = TYPE_FIELDS(node); field; field = TREE_CHAIN(field)) {
435 const_tree fieldtype = get_field_type(field);
436 enum tree_code code = TREE_CODE(fieldtype);
437
438 if (node == fieldtype)
439 continue;
440
441 if (!is_fptr(fieldtype))
442 return 0;
443
444 if (code != RECORD_TYPE && code != UNION_TYPE)
445 continue;
446
447 if (!is_pure_ops_struct(fieldtype))
448 return 0;
449 }
450
451 return 1;
452}
453
454static void randomize_type(tree type)
455{
456 tree variant;
457
458 gcc_assert(TREE_CODE(type) == RECORD_TYPE);
459
460 if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
461 return;
462
463 if (lookup_attribute("randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))) || is_pure_ops_struct(type))
464 relayout_struct(type);
465
466 for (variant = TYPE_MAIN_VARIANT(type); variant; variant = TYPE_NEXT_VARIANT(variant)) {
467 TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
468 TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("randomize_considered"), NULL_TREE, TYPE_ATTRIBUTES(type));
469 }
470#ifdef __DEBUG_PLUGIN
471 fprintf(stderr, "Marking randomize_considered on struct %s\n", ORIG_TYPE_NAME(type));
472#ifdef __DEBUG_VERBOSE
473 debug_tree(type);
474#endif
475#endif
476}
477
478static void update_decl_size(tree decl)
479{
480 tree lastval, lastidx, field, init, type, flexsize;
481 unsigned HOST_WIDE_INT len;
482
483 type = TREE_TYPE(decl);
484
485 if (!lookup_attribute("has_flexarray", TYPE_ATTRIBUTES(type)))
486 return;
487
488 init = DECL_INITIAL(decl);
489 if (init == NULL_TREE || init == error_mark_node)
490 return;
491
492 if (TREE_CODE(init) != CONSTRUCTOR)
493 return;
494
495 len = CONSTRUCTOR_NELTS(init);
496 if (!len)
497 return;
498
499 lastval = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->value;
500 lastidx = CONSTRUCTOR_ELT(init, CONSTRUCTOR_NELTS(init) - 1)->index;
501
502 for (field = TYPE_FIELDS(TREE_TYPE(decl)); TREE_CHAIN(field); field = TREE_CHAIN(field))
503 ;
504
505 if (lastidx != field)
506 return;
507
508 if (TREE_CODE(lastval) != STRING_CST) {
509 error("Only string constants are supported as initializers "
510 "for randomized structures with flexible arrays");
511 return;
512 }
513
514 flexsize = bitsize_int(TREE_STRING_LENGTH(lastval) *
515 tree_to_uhwi(TYPE_SIZE(TREE_TYPE(TREE_TYPE(lastval)))));
516
517 DECL_SIZE(decl) = size_binop(PLUS_EXPR, TYPE_SIZE(type), flexsize);
518
519 return;
520}
521
522
523static void randomize_layout_finish_decl(void *event_data, void *data)
524{
525 tree decl = (tree)event_data;
526 tree type;
527
528 if (decl == NULL_TREE || decl == error_mark_node)
529 return;
530
531 type = TREE_TYPE(decl);
532
533 if (TREE_CODE(decl) != VAR_DECL)
534 return;
535
536 if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
537 return;
538
539 if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(type)))
540 return;
541
542 DECL_SIZE(decl) = 0;
543 DECL_SIZE_UNIT(decl) = 0;
544 SET_DECL_ALIGN(decl, 0);
545 SET_DECL_MODE (decl, VOIDmode);
546 SET_DECL_RTL(decl, 0);
547 update_decl_size(decl);
548 layout_decl(decl, 0);
549}
550
551static void finish_type(void *event_data, void *data)
552{
553 tree type = (tree)event_data;
554
555 if (type == NULL_TREE || type == error_mark_node)
556 return;
557
558 if (TREE_CODE(type) != RECORD_TYPE)
559 return;
560
561 if (TYPE_FIELDS(type) == NULL_TREE)
562 return;
563
564 if (lookup_attribute("randomize_considered", TYPE_ATTRIBUTES(type)))
565 return;
566
567#ifdef __DEBUG_PLUGIN
568 fprintf(stderr, "Calling randomize_type on %s\n", ORIG_TYPE_NAME(type));
569#endif
570#ifdef __DEBUG_VERBOSE
571 debug_tree(type);
572#endif
573 randomize_type(type);
574
575 return;
576}
577
578static struct attribute_spec randomize_layout_attr = {
579 .name = "randomize_layout",
580 // related to args
581 .min_length = 0,
582 .max_length = 0,
583 .decl_required = false,
584 // need type declaration
585 .type_required = true,
586 .function_type_required = false,
587 .handler = handle_randomize_layout_attr,
588#if BUILDING_GCC_VERSION >= 4007
589 .affects_type_identity = true
590#endif
591};
592
593static struct attribute_spec no_randomize_layout_attr = {
594 .name = "no_randomize_layout",
595 // related to args
596 .min_length = 0,
597 .max_length = 0,
598 .decl_required = false,
599 // need type declaration
600 .type_required = true,
601 .function_type_required = false,
602 .handler = handle_randomize_layout_attr,
603#if BUILDING_GCC_VERSION >= 4007
604 .affects_type_identity = true
605#endif
606};
607
608static struct attribute_spec randomize_considered_attr = {
609 .name = "randomize_considered",
610 // related to args
611 .min_length = 0,
612 .max_length = 0,
613 .decl_required = false,
614 // need type declaration
615 .type_required = true,
616 .function_type_required = false,
617 .handler = handle_randomize_considered_attr,
618#if BUILDING_GCC_VERSION >= 4007
619 .affects_type_identity = false
620#endif
621};
622
623static struct attribute_spec randomize_performed_attr = {
624 .name = "randomize_performed",
625 // related to args
626 .min_length = 0,
627 .max_length = 0,
628 .decl_required = false,
629 // need type declaration
630 .type_required = true,
631 .function_type_required = false,
632 .handler = handle_randomize_performed_attr,
633#if BUILDING_GCC_VERSION >= 4007
634 .affects_type_identity = false
635#endif
636};
637
638static void register_attributes(void *event_data, void *data)
639{
640 register_attribute(&randomize_layout_attr);
641 register_attribute(&no_randomize_layout_attr);
642 register_attribute(&randomize_considered_attr);
643 register_attribute(&randomize_performed_attr);
644}
645
646static void check_bad_casts_in_constructor(tree var, tree init)
647{
648 unsigned HOST_WIDE_INT idx;
649 tree field, val;
650 tree field_type, val_type;
651
652 FOR_EACH_CONSTRUCTOR_ELT(CONSTRUCTOR_ELTS(init), idx, field, val) {
653 if (TREE_CODE(val) == CONSTRUCTOR) {
654 check_bad_casts_in_constructor(var, val);
655 continue;
656 }
657
658 /* pipacs' plugin creates franken-arrays that differ from those produced by
659 normal code which all have valid 'field' trees. work around this */
660 if (field == NULL_TREE)
661 continue;
662 field_type = TREE_TYPE(field);
663 val_type = TREE_TYPE(val);
664
665 if (TREE_CODE(field_type) != POINTER_TYPE || TREE_CODE(val_type) != POINTER_TYPE)
666 continue;
667
668 if (field_type == val_type)
669 continue;
670
671 field_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(field_type))));
672 val_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(val_type))));
673
674 if (field_type == void_type_node)
675 continue;
676 if (field_type == val_type)
677 continue;
678 if (TREE_CODE(val_type) != RECORD_TYPE)
679 continue;
680
681 if (!lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(val_type)))
682 continue;
683 MISMATCH(DECL_SOURCE_LOCATION(var), "constructor\n", TYPE_MAIN_VARIANT(field_type), TYPE_MAIN_VARIANT(val_type));
684 }
685}
686
687/* derived from the constify plugin */
688static void check_global_variables(void *event_data, void *data)
689{
690 struct varpool_node *node;
691 tree init;
692
693 FOR_EACH_VARIABLE(node) {
694 tree var = NODE_DECL(node);
695 init = DECL_INITIAL(var);
696 if (init == NULL_TREE)
697 continue;
698
699 if (TREE_CODE(init) != CONSTRUCTOR)
700 continue;
701
702 check_bad_casts_in_constructor(var, init);
703 }
704}
705
706static bool dominated_by_is_err(const_tree rhs, basic_block bb)
707{
708 basic_block dom;
709 gimple dom_stmt;
710 gimple call_stmt;
711 const_tree dom_lhs;
712 const_tree poss_is_err_cond;
713 const_tree poss_is_err_func;
714 const_tree is_err_arg;
715
716 dom = get_immediate_dominator(CDI_DOMINATORS, bb);
717 if (!dom)
718 return false;
719
720 dom_stmt = last_stmt(dom);
721 if (!dom_stmt)
722 return false;
723
724 if (gimple_code(dom_stmt) != GIMPLE_COND)
725 return false;
726
727 if (gimple_cond_code(dom_stmt) != NE_EXPR)
728 return false;
729
730 if (!integer_zerop(gimple_cond_rhs(dom_stmt)))
731 return false;
732
733 poss_is_err_cond = gimple_cond_lhs(dom_stmt);
734
735 if (TREE_CODE(poss_is_err_cond) != SSA_NAME)
736 return false;
737
738 call_stmt = SSA_NAME_DEF_STMT(poss_is_err_cond);
739
740 if (gimple_code(call_stmt) != GIMPLE_CALL)
741 return false;
742
743 dom_lhs = gimple_get_lhs(call_stmt);
744 poss_is_err_func = gimple_call_fndecl(call_stmt);
745 if (!poss_is_err_func)
746 return false;
747 if (dom_lhs != poss_is_err_cond)
748 return false;
749 if (strcmp(DECL_NAME_POINTER(poss_is_err_func), "IS_ERR"))
750 return false;
751
752 is_err_arg = gimple_call_arg(call_stmt, 0);
753 if (!is_err_arg)
754 return false;
755
756 if (is_err_arg != rhs)
757 return false;
758
759 return true;
760}
761
762static void handle_local_var_initializers(void)
763{
764 tree var;
765 unsigned int i;
766
767 FOR_EACH_LOCAL_DECL(cfun, i, var) {
768 tree init = DECL_INITIAL(var);
769 if (!init)
770 continue;
771 if (TREE_CODE(init) != CONSTRUCTOR)
772 continue;
773 check_bad_casts_in_constructor(var, init);
774 }
775}
776
777static bool type_name_eq(gimple stmt, const_tree type_tree, const char *wanted_name)
778{
779 const char *type_name;
780
781 if (type_tree == NULL_TREE)
782 return false;
783
784 switch (TREE_CODE(type_tree)) {
785 case RECORD_TYPE:
786 type_name = TYPE_NAME_POINTER(type_tree);
787 break;
788 case INTEGER_TYPE:
789 if (TYPE_PRECISION(type_tree) == CHAR_TYPE_SIZE)
790 type_name = "char";
791 else {
792 INFORM(gimple_location(stmt), "found non-char INTEGER_TYPE cast comparison: %qT\n", type_tree);
793 debug_tree(type_tree);
794 return false;
795 }
796 break;
797 case POINTER_TYPE:
798 if (TREE_CODE(TREE_TYPE(type_tree)) == VOID_TYPE) {
799 type_name = "void *";
800 break;
801 } else {
802 INFORM(gimple_location(stmt), "found non-void POINTER_TYPE cast comparison %qT\n", type_tree);
803 debug_tree(type_tree);
804 return false;
805 }
806 default:
807 INFORM(gimple_location(stmt), "unhandled cast comparison: %qT\n", type_tree);
808 debug_tree(type_tree);
809 return false;
810 }
811
812 return strcmp(type_name, wanted_name) == 0;
813}
814
815static bool whitelisted_cast(gimple stmt, const_tree lhs_tree, const_tree rhs_tree)
816{
817 const struct whitelist_entry *entry;
818 expanded_location xloc = expand_location(gimple_location(stmt));
819
820 for (entry = whitelist; entry->pathname; entry++) {
821 if (!strstr(xloc.file, entry->pathname))
822 continue;
823
824 if (type_name_eq(stmt, lhs_tree, entry->lhs) && type_name_eq(stmt, rhs_tree, entry->rhs))
825 return true;
826 }
827
828 return false;
829}
830
831/*
832 * iterate over all statements to find "bad" casts:
833 * those where the address of the start of a structure is cast
834 * to a pointer of a structure of a different type, or a
835 * structure pointer type is cast to a different structure pointer type
836 */
837static unsigned int find_bad_casts_execute(void)
838{
839 basic_block bb;
840
841 handle_local_var_initializers();
842
843 FOR_EACH_BB_FN(bb, cfun) {
844 gimple_stmt_iterator gsi;
845
846 for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
847 gimple stmt;
848 const_tree lhs;
849 const_tree lhs_type;
850 const_tree rhs1;
851 const_tree rhs_type;
852 const_tree ptr_lhs_type;
853 const_tree ptr_rhs_type;
854 const_tree op0;
855 const_tree op0_type;
856 enum tree_code rhs_code;
857
858 stmt = gsi_stmt(gsi);
859
860#ifdef __DEBUG_PLUGIN
861#ifdef __DEBUG_VERBOSE
862 debug_gimple_stmt(stmt);
863 debug_tree(gimple_get_lhs(stmt));
864#endif
865#endif
866
867 if (gimple_code(stmt) != GIMPLE_ASSIGN)
868 continue;
869
870#ifdef __DEBUG_PLUGIN
871#ifdef __DEBUG_VERBOSE
872 debug_tree(gimple_assign_rhs1(stmt));
873#endif
874#endif
875
876
877 rhs_code = gimple_assign_rhs_code(stmt);
878
879 if (rhs_code != ADDR_EXPR && rhs_code != SSA_NAME)
880 continue;
881
882 lhs = gimple_get_lhs(stmt);
883 lhs_type = TREE_TYPE(lhs);
884 rhs1 = gimple_assign_rhs1(stmt);
885 rhs_type = TREE_TYPE(rhs1);
886
887 if (TREE_CODE(rhs_type) != POINTER_TYPE ||
888 TREE_CODE(lhs_type) != POINTER_TYPE)
889 continue;
890
891 ptr_lhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(lhs_type))));
892 ptr_rhs_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(rhs_type))));
893
894 if (ptr_rhs_type == void_type_node)
895 continue;
896
897 if (ptr_lhs_type == void_type_node)
898 continue;
899
900 if (dominated_by_is_err(rhs1, bb))
901 continue;
902
903 if (TREE_CODE(ptr_rhs_type) != RECORD_TYPE) {
904#ifndef __DEBUG_PLUGIN
905 if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_lhs_type)))
906#endif
907 {
908 if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
909 MISMATCH(gimple_location(stmt), "rhs", ptr_lhs_type, ptr_rhs_type);
910 }
911 continue;
912 }
913
914 if (rhs_code == SSA_NAME && ptr_lhs_type == ptr_rhs_type)
915 continue;
916
917 if (rhs_code == ADDR_EXPR) {
918 op0 = TREE_OPERAND(rhs1, 0);
919
920 if (op0 == NULL_TREE)
921 continue;
922
923 if (TREE_CODE(op0) != VAR_DECL)
924 continue;
925
926 op0_type = TYPE_MAIN_VARIANT(strip_array_types(TYPE_MAIN_VARIANT(TREE_TYPE(op0))));
927 if (op0_type == ptr_lhs_type)
928 continue;
929
930#ifndef __DEBUG_PLUGIN
931 if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(op0_type)))
932#endif
933 {
934 if (!whitelisted_cast(stmt, ptr_lhs_type, op0_type))
935 MISMATCH(gimple_location(stmt), "op0", ptr_lhs_type, op0_type);
936 }
937 } else {
938 const_tree ssa_name_var = SSA_NAME_VAR(rhs1);
939 /* skip bogus type casts introduced by container_of */
940 if (ssa_name_var != NULL_TREE && DECL_NAME(ssa_name_var) &&
941 !strcmp((const char *)DECL_NAME_POINTER(ssa_name_var), "__mptr"))
942 continue;
943#ifndef __DEBUG_PLUGIN
944 if (lookup_attribute("randomize_performed", TYPE_ATTRIBUTES(ptr_rhs_type)))
945#endif
946 {
947 if (!whitelisted_cast(stmt, ptr_lhs_type, ptr_rhs_type))
948 MISMATCH(gimple_location(stmt), "ssa", ptr_lhs_type, ptr_rhs_type);
949 }
950 }
951
952 }
953 }
954 return 0;
955}
956
957#define PASS_NAME find_bad_casts
958#define NO_GATE
959#define TODO_FLAGS_FINISH TODO_dump_func
960#include "gcc-generate-gimple-pass.h"
961
962__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
963{
964 int i;
965 const char * const plugin_name = plugin_info->base_name;
966 const int argc = plugin_info->argc;
967 const struct plugin_argument * const argv = plugin_info->argv;
968 bool enable = true;
969 int obtained_seed = 0;
970 struct register_pass_info find_bad_casts_pass_info;
971
972 find_bad_casts_pass_info.pass = make_find_bad_casts_pass();
973 find_bad_casts_pass_info.reference_pass_name = "ssa";
974 find_bad_casts_pass_info.ref_pass_instance_number = 1;
975 find_bad_casts_pass_info.pos_op = PASS_POS_INSERT_AFTER;
976
977 if (!plugin_default_version_check(version, &gcc_version)) {
978 error(G_("incompatible gcc/plugin versions"));
979 return 1;
980 }
981
982 if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
983 inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
984 enable = false;
985 }
986
987 for (i = 0; i < argc; ++i) {
988 if (!strcmp(argv[i].key, "disable")) {
989 enable = false;
990 continue;
991 }
992 if (!strcmp(argv[i].key, "performance-mode")) {
993 performance_mode = 1;
994 continue;
995 }
996 error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
997 }
998
999 if (strlen(randstruct_seed) != 64) {
1000 error(G_("invalid seed value supplied for %s plugin"), plugin_name);
1001 return 1;
1002 }
1003 obtained_seed = sscanf(randstruct_seed, "%016llx%016llx%016llx%016llx",
1004 &shuffle_seed[0], &shuffle_seed[1], &shuffle_seed[2], &shuffle_seed[3]);
1005 if (obtained_seed != 4) {
1006 error(G_("Invalid seed supplied for %s plugin"), plugin_name);
1007 return 1;
1008 }
1009
1010 register_callback(plugin_name, PLUGIN_INFO, NULL, &randomize_layout_plugin_info);
1011 if (enable) {
1012 register_callback(plugin_name, PLUGIN_ALL_IPA_PASSES_START, check_global_variables, NULL);
1013 register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &find_bad_casts_pass_info);
1014 register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
1015 register_callback(plugin_name, PLUGIN_FINISH_DECL, randomize_layout_finish_decl, NULL);
1016 }
1017 register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
1018
1019 return 0;
1020}