aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/Kconfig.debug10
-rw-r--r--lib/Makefile1
-rw-r--r--lib/test_stackinit.c378
-rw-r--r--scripts/Makefile.gcc-plugins2
-rw-r--r--scripts/gcc-plugins/Kconfig58
-rw-r--r--scripts/gcc-plugins/structleak_plugin.c36
6 files changed, 463 insertions, 22 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index d765d36f8737..e718487c97c3 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2016,6 +2016,16 @@ config TEST_OBJAGG
2016 (or module load). 2016 (or module load).
2017 2017
2018 2018
2019config TEST_STACKINIT
2020 tristate "Test level of stack variable initialization"
2021 help
2022 Test if the kernel is zero-initializing stack variables and
2023 padding. Coverage is controlled by compiler flags,
2024 CONFIG_GCC_PLUGIN_STRUCTLEAK, CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF,
2025 or CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL.
2026
2027 If unsure, say N.
2028
2019endif # RUNTIME_TESTING_MENU 2029endif # RUNTIME_TESTING_MENU
2020 2030
2021config MEMTEST 2031config MEMTEST
diff --git a/lib/Makefile b/lib/Makefile
index c7ae0b3e4535..647517940b29 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_TEST_KMOD) += test_kmod.o
77obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o 77obj-$(CONFIG_TEST_DEBUG_VIRTUAL) += test_debug_virtual.o
78obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o 78obj-$(CONFIG_TEST_MEMCAT_P) += test_memcat_p.o
79obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o 79obj-$(CONFIG_TEST_OBJAGG) += test_objagg.o
80obj-$(CONFIG_TEST_STACKINIT) += test_stackinit.o
80 81
81obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/ 82obj-$(CONFIG_TEST_LIVEPATCH) += livepatch/
82 83
diff --git a/lib/test_stackinit.c b/lib/test_stackinit.c
new file mode 100644
index 000000000000..13115b6f2b88
--- /dev/null
+++ b/lib/test_stackinit.c
@@ -0,0 +1,378 @@
1// SPDX-Licenses: GPLv2
2/*
3 * Test cases for compiler-based stack variable zeroing via future
4 * compiler flags or CONFIG_GCC_PLUGIN_STRUCTLEAK*.
5 */
6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
7
8#include <linux/init.h>
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/string.h>
12
13/* Exfiltration buffer. */
14#define MAX_VAR_SIZE 128
15static char check_buf[MAX_VAR_SIZE];
16
17/* Character array to trigger stack protector in all functions. */
18#define VAR_BUFFER 32
19
20/* Volatile mask to convince compiler to copy memory with 0xff. */
21static volatile u8 forced_mask = 0xff;
22
23/* Location and size tracking to validate fill and test are colocated. */
24static void *fill_start, *target_start;
25static size_t fill_size, target_size;
26
27static bool range_contains(char *haystack_start, size_t haystack_size,
28 char *needle_start, size_t needle_size)
29{
30 if (needle_start >= haystack_start &&
31 needle_start + needle_size <= haystack_start + haystack_size)
32 return true;
33 return false;
34}
35
36#define DO_NOTHING_TYPE_SCALAR(var_type) var_type
37#define DO_NOTHING_TYPE_STRING(var_type) void
38#define DO_NOTHING_TYPE_STRUCT(var_type) void
39
40#define DO_NOTHING_RETURN_SCALAR(ptr) *(ptr)
41#define DO_NOTHING_RETURN_STRING(ptr) /**/
42#define DO_NOTHING_RETURN_STRUCT(ptr) /**/
43
44#define DO_NOTHING_CALL_SCALAR(var, name) \
45 (var) = do_nothing_ ## name(&(var))
46#define DO_NOTHING_CALL_STRING(var, name) \
47 do_nothing_ ## name(var)
48#define DO_NOTHING_CALL_STRUCT(var, name) \
49 do_nothing_ ## name(&(var))
50
51#define FETCH_ARG_SCALAR(var) &var
52#define FETCH_ARG_STRING(var) var
53#define FETCH_ARG_STRUCT(var) &var
54
55#define FILL_SIZE_STRING 16
56
57#define INIT_CLONE_SCALAR /**/
58#define INIT_CLONE_STRING [FILL_SIZE_STRING]
59#define INIT_CLONE_STRUCT /**/
60
61#define INIT_SCALAR_none /**/
62#define INIT_SCALAR_zero = 0
63
64#define INIT_STRING_none [FILL_SIZE_STRING] /**/
65#define INIT_STRING_zero [FILL_SIZE_STRING] = { }
66
67#define INIT_STRUCT_none /**/
68#define INIT_STRUCT_zero = { }
69#define INIT_STRUCT_static_partial = { .two = 0, }
70#define INIT_STRUCT_static_all = { .one = arg->one, \
71 .two = arg->two, \
72 .three = arg->three, \
73 .four = arg->four, \
74 }
75#define INIT_STRUCT_dynamic_partial = { .two = arg->two, }
76#define INIT_STRUCT_dynamic_all = { .one = arg->one, \
77 .two = arg->two, \
78 .three = arg->three, \
79 .four = arg->four, \
80 }
81#define INIT_STRUCT_runtime_partial ; \
82 var.two = 0
83#define INIT_STRUCT_runtime_all ; \
84 var.one = 0; \
85 var.two = 0; \
86 var.three = 0; \
87 memset(&var.four, 0, \
88 sizeof(var.four))
89
90/*
91 * @name: unique string name for the test
92 * @var_type: type to be tested for zeroing initialization
93 * @which: is this a SCALAR, STRING, or STRUCT type?
94 * @init_level: what kind of initialization is performed
95 */
96#define DEFINE_TEST_DRIVER(name, var_type, which) \
97/* Returns 0 on success, 1 on failure. */ \
98static noinline __init int test_ ## name (void) \
99{ \
100 var_type zero INIT_CLONE_ ## which; \
101 int ignored; \
102 u8 sum = 0, i; \
103 \
104 /* Notice when a new test is larger than expected. */ \
105 BUILD_BUG_ON(sizeof(zero) > MAX_VAR_SIZE); \
106 \
107 /* Fill clone type with zero for per-field init. */ \
108 memset(&zero, 0x00, sizeof(zero)); \
109 /* Fill stack with 0xFF. */ \
110 ignored = leaf_ ##name((unsigned long)&ignored, 1, \
111 FETCH_ARG_ ## which(zero)); \
112 /* Clear entire check buffer for later bit tests. */ \
113 memset(check_buf, 0x00, sizeof(check_buf)); \
114 /* Extract stack-defined variable contents. */ \
115 ignored = leaf_ ##name((unsigned long)&ignored, 0, \
116 FETCH_ARG_ ## which(zero)); \
117 \
118 /* Validate that compiler lined up fill and target. */ \
119 if (!range_contains(fill_start, fill_size, \
120 target_start, target_size)) { \
121 pr_err(#name ": stack fill missed target!?\n"); \
122 pr_err(#name ": fill %zu wide\n", fill_size); \
123 pr_err(#name ": target offset by %d\n", \
124 (int)((ssize_t)(uintptr_t)fill_start - \
125 (ssize_t)(uintptr_t)target_start)); \
126 return 1; \
127 } \
128 \
129 /* Look for any set bits in the check region. */ \
130 for (i = 0; i < sizeof(check_buf); i++) \
131 sum += (check_buf[i] != 0); \
132 \
133 if (sum == 0) \
134 pr_info(#name " ok\n"); \
135 else \
136 pr_warn(#name " FAIL (uninit bytes: %d)\n", \
137 sum); \
138 \
139 return (sum != 0); \
140}
141#define DEFINE_TEST(name, var_type, which, init_level) \
142/* no-op to force compiler into ignoring "uninitialized" vars */\
143static noinline __init DO_NOTHING_TYPE_ ## which(var_type) \
144do_nothing_ ## name(var_type *ptr) \
145{ \
146 /* Will always be true, but compiler doesn't know. */ \
147 if ((unsigned long)ptr > 0x2) \
148 return DO_NOTHING_RETURN_ ## which(ptr); \
149 else \
150 return DO_NOTHING_RETURN_ ## which(ptr + 1); \
151} \
152static noinline __init int leaf_ ## name(unsigned long sp, \
153 bool fill, \
154 var_type *arg) \
155{ \
156 char buf[VAR_BUFFER]; \
157 var_type var INIT_ ## which ## _ ## init_level; \
158 \
159 target_start = &var; \
160 target_size = sizeof(var); \
161 /* \
162 * Keep this buffer around to make sure we've got a \
163 * stack frame of SOME kind... \
164 */ \
165 memset(buf, (char)(sp && 0xff), sizeof(buf)); \
166 /* Fill variable with 0xFF. */ \
167 if (fill) { \
168 fill_start = &var; \
169 fill_size = sizeof(var); \
170 memset(fill_start, \
171 (char)((sp && 0xff) | forced_mask), \
172 fill_size); \
173 } \
174 \
175 /* Silence "never initialized" warnings. */ \
176 DO_NOTHING_CALL_ ## which(var, name); \
177 \
178 /* Exfiltrate "var". */ \
179 memcpy(check_buf, target_start, target_size); \
180 \
181 return (int)buf[0] | (int)buf[sizeof(buf) - 1]; \
182} \
183DEFINE_TEST_DRIVER(name, var_type, which)
184
185/* Structure with no padding. */
186struct test_packed {
187 unsigned long one;
188 unsigned long two;
189 unsigned long three;
190 unsigned long four;
191};
192
193/* Simple structure with padding likely to be covered by compiler. */
194struct test_small_hole {
195 size_t one;
196 char two;
197 /* 3 byte padding hole here. */
198 int three;
199 unsigned long four;
200};
201
202/* Try to trigger unhandled padding in a structure. */
203struct test_aligned {
204 u32 internal1;
205 u64 internal2;
206} __aligned(64);
207
208struct test_big_hole {
209 u8 one;
210 u8 two;
211 u8 three;
212 /* 61 byte padding hole here. */
213 struct test_aligned four;
214} __aligned(64);
215
216struct test_trailing_hole {
217 char *one;
218 char *two;
219 char *three;
220 char four;
221 /* "sizeof(unsigned long) - 1" byte padding hole here. */
222};
223
224/* Test if STRUCTLEAK is clearing structs with __user fields. */
225struct test_user {
226 u8 one;
227 unsigned long two;
228 char __user *three;
229 unsigned long four;
230};
231
232#define DEFINE_SCALAR_TEST(name, init) \
233 DEFINE_TEST(name ## _ ## init, name, SCALAR, init)
234
235#define DEFINE_SCALAR_TESTS(init) \
236 DEFINE_SCALAR_TEST(u8, init); \
237 DEFINE_SCALAR_TEST(u16, init); \
238 DEFINE_SCALAR_TEST(u32, init); \
239 DEFINE_SCALAR_TEST(u64, init); \
240 DEFINE_TEST(char_array_ ## init, unsigned char, STRING, init)
241
242#define DEFINE_STRUCT_TEST(name, init) \
243 DEFINE_TEST(name ## _ ## init, \
244 struct test_ ## name, STRUCT, init)
245
246#define DEFINE_STRUCT_TESTS(init) \
247 DEFINE_STRUCT_TEST(small_hole, init); \
248 DEFINE_STRUCT_TEST(big_hole, init); \
249 DEFINE_STRUCT_TEST(trailing_hole, init); \
250 DEFINE_STRUCT_TEST(packed, init)
251
252/* These should be fully initialized all the time! */
253DEFINE_SCALAR_TESTS(zero);
254DEFINE_STRUCT_TESTS(zero);
255/* Static initialization: padding may be left uninitialized. */
256DEFINE_STRUCT_TESTS(static_partial);
257DEFINE_STRUCT_TESTS(static_all);
258/* Dynamic initialization: padding may be left uninitialized. */
259DEFINE_STRUCT_TESTS(dynamic_partial);
260DEFINE_STRUCT_TESTS(dynamic_all);
261/* Runtime initialization: padding may be left uninitialized. */
262DEFINE_STRUCT_TESTS(runtime_partial);
263DEFINE_STRUCT_TESTS(runtime_all);
264/* No initialization without compiler instrumentation. */
265DEFINE_SCALAR_TESTS(none);
266DEFINE_STRUCT_TESTS(none);
267DEFINE_TEST(user, struct test_user, STRUCT, none);
268
269/*
270 * Check two uses through a variable declaration outside either path,
271 * which was noticed as a special case in porting earlier stack init
272 * compiler logic.
273 */
274static int noinline __leaf_switch_none(int path, bool fill)
275{
276 switch (path) {
277 uint64_t var;
278
279 case 1:
280 target_start = &var;
281 target_size = sizeof(var);
282 if (fill) {
283 fill_start = &var;
284 fill_size = sizeof(var);
285
286 memset(fill_start, forced_mask | 0x55, fill_size);
287 }
288 memcpy(check_buf, target_start, target_size);
289 break;
290 case 2:
291 target_start = &var;
292 target_size = sizeof(var);
293 if (fill) {
294 fill_start = &var;
295 fill_size = sizeof(var);
296
297 memset(fill_start, forced_mask | 0xaa, fill_size);
298 }
299 memcpy(check_buf, target_start, target_size);
300 break;
301 default:
302 var = 5;
303 return var & forced_mask;
304 }
305 return 0;
306}
307
308static noinline __init int leaf_switch_1_none(unsigned long sp, bool fill,
309 uint64_t *arg)
310{
311 return __leaf_switch_none(1, fill);
312}
313
314static noinline __init int leaf_switch_2_none(unsigned long sp, bool fill,
315 uint64_t *arg)
316{
317 return __leaf_switch_none(2, fill);
318}
319
320DEFINE_TEST_DRIVER(switch_1_none, uint64_t, SCALAR);
321DEFINE_TEST_DRIVER(switch_2_none, uint64_t, SCALAR);
322
323static int __init test_stackinit_init(void)
324{
325 unsigned int failures = 0;
326
327#define test_scalars(init) do { \
328 failures += test_u8_ ## init (); \
329 failures += test_u16_ ## init (); \
330 failures += test_u32_ ## init (); \
331 failures += test_u64_ ## init (); \
332 failures += test_char_array_ ## init (); \
333 } while (0)
334
335#define test_structs(init) do { \
336 failures += test_small_hole_ ## init (); \
337 failures += test_big_hole_ ## init (); \
338 failures += test_trailing_hole_ ## init (); \
339 failures += test_packed_ ## init (); \
340 } while (0)
341
342 /* These are explicitly initialized and should always pass. */
343 test_scalars(zero);
344 test_structs(zero);
345 /* Padding here appears to be accidentally always initialized? */
346 test_structs(dynamic_partial);
347 /* Padding initialization depends on compiler behaviors. */
348 test_structs(static_partial);
349 test_structs(static_all);
350 test_structs(dynamic_all);
351 test_structs(runtime_partial);
352 test_structs(runtime_all);
353
354 /* STRUCTLEAK_BYREF_ALL should cover everything from here down. */
355 test_scalars(none);
356 failures += test_switch_1_none();
357 failures += test_switch_2_none();
358
359 /* STRUCTLEAK_BYREF should cover from here down. */
360 test_structs(none);
361
362 /* STRUCTLEAK will only cover this. */
363 failures += test_user();
364
365 if (failures == 0)
366 pr_info("all tests passed!\n");
367 else
368 pr_err("failures: %u\n", failures);
369
370 return failures ? -EINVAL : 0;
371}
372module_init(test_stackinit_init);
373
374static void __exit test_stackinit_exit(void)
375{ }
376module_exit(test_stackinit_exit);
377
378MODULE_LICENSE("GPL");
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index 35042d96cf5d..5f7df50cfe7a 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -15,6 +15,8 @@ gcc-plugin-$(CONFIG_GCC_PLUGIN_SANCOV) += sancov_plugin.so
15gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so 15gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so
16gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) \ 16gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) \
17 += -fplugin-arg-structleak_plugin-verbose 17 += -fplugin-arg-structleak_plugin-verbose
18gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF) \
19 += -fplugin-arg-structleak_plugin-byref
18gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL) \ 20gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL) \
19 += -fplugin-arg-structleak_plugin-byref-all 21 += -fplugin-arg-structleak_plugin-byref-all
20gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) \ 22gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) \
diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig
index d9fd9988ef27..74271dba4f94 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -67,23 +67,59 @@ config GCC_PLUGIN_LATENT_ENTROPY
67 * https://pax.grsecurity.net/ 67 * https://pax.grsecurity.net/
68 68
69config GCC_PLUGIN_STRUCTLEAK 69config GCC_PLUGIN_STRUCTLEAK
70 bool "Force initialization of variables containing userspace addresses" 70 bool "Zero initialize stack variables"
71 help 71 help
72 This plugin zero-initializes any structures containing a 72 While the kernel is built with warnings enabled for any missed
73 __user attribute. This can prevent some classes of information 73 stack variable initializations, this warning is silenced for
74 exposures. 74 anything passed by reference to another function, under the
75 75 occasionally misguided assumption that the function will do
76 This plugin was ported from grsecurity/PaX. More information at: 76 the initialization. As this regularly leads to exploitable
77 flaws, this plugin is available to identify and zero-initialize
78 such variables, depending on the chosen level of coverage.
79
80 This plugin was originally ported from grsecurity/PaX. More
81 information at:
77 * https://grsecurity.net/ 82 * https://grsecurity.net/
78 * https://pax.grsecurity.net/ 83 * https://pax.grsecurity.net/
79 84
80config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL 85choice
81 bool "Force initialize all struct type variables passed by reference" 86 prompt "Coverage"
82 depends on GCC_PLUGIN_STRUCTLEAK 87 depends on GCC_PLUGIN_STRUCTLEAK
83 depends on !COMPILE_TEST 88 default GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
84 help 89 help
85 Zero initialize any struct type local variable that may be passed by 90 This chooses the level of coverage over classes of potentially
86 reference without having been initialized. 91 uninitialized variables. The selected class will be
92 zero-initialized before use.
93
94 config GCC_PLUGIN_STRUCTLEAK_USER
95 bool "structs marked for userspace"
96 help
97 Zero-initialize any structures on the stack containing
98 a __user attribute. This can prevent some classes of
99 uninitialized stack variable exploits and information
100 exposures, like CVE-2013-2141:
101 https://git.kernel.org/linus/b9e146d8eb3b9eca
102
103 config GCC_PLUGIN_STRUCTLEAK_BYREF
104 bool "structs passed by reference"
105 help
106 Zero-initialize any structures on the stack that may
107 be passed by reference and had not already been
108 explicitly initialized. This can prevent most classes
109 of uninitialized stack variable exploits and information
110 exposures, like CVE-2017-1000410:
111 https://git.kernel.org/linus/06e7e776ca4d3654
112
113 config GCC_PLUGIN_STRUCTLEAK_BYREF_ALL
114 bool "anything passed by reference"
115 help
116 Zero-initialize any stack variables that may be passed
117 by reference and had not already been explicitly
118 initialized. This is intended to eliminate all classes
119 of uninitialized stack variable exploits and information
120 exposures.
121
122endchoice
87 123
88config GCC_PLUGIN_STRUCTLEAK_VERBOSE 124config GCC_PLUGIN_STRUCTLEAK_VERBOSE
89 bool "Report forcefully initialized variables" 125 bool "Report forcefully initialized variables"
diff --git a/scripts/gcc-plugins/structleak_plugin.c b/scripts/gcc-plugins/structleak_plugin.c
index 10292f791e99..e89be8f5c859 100644
--- a/scripts/gcc-plugins/structleak_plugin.c
+++ b/scripts/gcc-plugins/structleak_plugin.c
@@ -16,6 +16,7 @@
16 * Options: 16 * Options:
17 * -fplugin-arg-structleak_plugin-disable 17 * -fplugin-arg-structleak_plugin-disable
18 * -fplugin-arg-structleak_plugin-verbose 18 * -fplugin-arg-structleak_plugin-verbose
19 * -fplugin-arg-structleak_plugin-byref
19 * -fplugin-arg-structleak_plugin-byref-all 20 * -fplugin-arg-structleak_plugin-byref-all
20 * 21 *
21 * Usage: 22 * Usage:
@@ -26,7 +27,6 @@
26 * $ gcc -fplugin=./structleak_plugin.so test.c -O2 27 * $ gcc -fplugin=./structleak_plugin.so test.c -O2
27 * 28 *
28 * TODO: eliminate redundant initializers 29 * TODO: eliminate redundant initializers
29 * increase type coverage
30 */ 30 */
31 31
32#include "gcc-common.h" 32#include "gcc-common.h"
@@ -37,13 +37,18 @@
37__visible int plugin_is_GPL_compatible; 37__visible int plugin_is_GPL_compatible;
38 38
39static struct plugin_info structleak_plugin_info = { 39static struct plugin_info structleak_plugin_info = {
40 .version = "201607271510vanilla", 40 .version = "20190125vanilla",
41 .help = "disable\tdo not activate plugin\n" 41 .help = "disable\tdo not activate plugin\n"
42 "verbose\tprint all initialized variables\n", 42 "byref\tinit structs passed by reference\n"
43 "byref-all\tinit anything passed by reference\n"
44 "verbose\tprint all initialized variables\n",
43}; 45};
44 46
47#define BYREF_STRUCT 1
48#define BYREF_ALL 2
49
45static bool verbose; 50static bool verbose;
46static bool byref_all; 51static int byref;
47 52
48static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs) 53static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
49{ 54{
@@ -118,6 +123,7 @@ static void initialize(tree var)
118 gimple_stmt_iterator gsi; 123 gimple_stmt_iterator gsi;
119 tree initializer; 124 tree initializer;
120 gimple init_stmt; 125 gimple init_stmt;
126 tree type;
121 127
122 /* this is the original entry bb before the forced split */ 128 /* this is the original entry bb before the forced split */
123 bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); 129 bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
@@ -148,11 +154,15 @@ static void initialize(tree var)
148 if (verbose) 154 if (verbose)
149 inform(DECL_SOURCE_LOCATION(var), 155 inform(DECL_SOURCE_LOCATION(var),
150 "%s variable will be forcibly initialized", 156 "%s variable will be forcibly initialized",
151 (byref_all && TREE_ADDRESSABLE(var)) ? "byref" 157 (byref && TREE_ADDRESSABLE(var)) ? "byref"
152 : "userspace"); 158 : "userspace");
153 159
154 /* build the initializer expression */ 160 /* build the initializer expression */
155 initializer = build_constructor(TREE_TYPE(var), NULL); 161 type = TREE_TYPE(var);
162 if (AGGREGATE_TYPE_P(type))
163 initializer = build_constructor(type, NULL);
164 else
165 initializer = fold_convert(type, integer_zero_node);
156 166
157 /* build the initializer stmt */ 167 /* build the initializer stmt */
158 init_stmt = gimple_build_assign(var, initializer); 168 init_stmt = gimple_build_assign(var, initializer);
@@ -184,13 +194,13 @@ static unsigned int structleak_execute(void)
184 if (!auto_var_in_fn_p(var, current_function_decl)) 194 if (!auto_var_in_fn_p(var, current_function_decl))
185 continue; 195 continue;
186 196
187 /* only care about structure types */ 197 /* only care about structure types unless byref-all */
188 if (TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE) 198 if (byref != BYREF_ALL && TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
189 continue; 199 continue;
190 200
191 /* if the type is of interest, examine the variable */ 201 /* if the type is of interest, examine the variable */
192 if (TYPE_USERSPACE(type) || 202 if (TYPE_USERSPACE(type) ||
193 (byref_all && TREE_ADDRESSABLE(var))) 203 (byref && TREE_ADDRESSABLE(var)))
194 initialize(var); 204 initialize(var);
195 } 205 }
196 206
@@ -232,8 +242,12 @@ __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gc
232 verbose = true; 242 verbose = true;
233 continue; 243 continue;
234 } 244 }
245 if (!strcmp(argv[i].key, "byref")) {
246 byref = BYREF_STRUCT;
247 continue;
248 }
235 if (!strcmp(argv[i].key, "byref-all")) { 249 if (!strcmp(argv[i].key, "byref-all")) {
236 byref_all = true; 250 byref = BYREF_ALL;
237 continue; 251 continue;
238 } 252 }
239 error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key); 253 error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);