1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30#include "gcc-common.h"
31
32__visible int plugin_is_GPL_compatible;
33
34static int track_frame_size = -1;
35static const char track_function[] = "stackleak_track_stack";
36
37
38
39
40
41static GTY(()) tree track_function_decl;
42
43static struct plugin_info stackleak_plugin_info = {
44 .version = "201707101337",
45 .help = "track-min-size=nn\ttrack stack for functions with a stack frame size >= nn bytes\n"
46 "disable\t\tdo not activate the plugin\n"
47};
48
49static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after)
50{
51 gimple stmt;
52 gcall *stackleak_track_stack;
53 cgraph_node_ptr node;
54 int frequency;
55 basic_block bb;
56
57
58 stmt = gimple_build_call(track_function_decl, 0);
59 stackleak_track_stack = as_a_gcall(stmt);
60 if (after) {
61 gsi_insert_after(gsi, stackleak_track_stack,
62 GSI_CONTINUE_LINKING);
63 } else {
64 gsi_insert_before(gsi, stackleak_track_stack, GSI_SAME_STMT);
65 }
66
67
68 bb = gimple_bb(stackleak_track_stack);
69 node = cgraph_get_create_node(track_function_decl);
70 gcc_assert(node);
71 frequency = compute_call_stmt_bb_frequency(current_function_decl, bb);
72 cgraph_create_edge(cgraph_get_node(current_function_decl), node,
73 stackleak_track_stack, bb->count, frequency);
74}
75
76static bool is_alloca(gimple stmt)
77{
78 if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA))
79 return true;
80
81#if BUILDING_GCC_VERSION >= 4007
82 if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
83 return true;
84#endif
85
86 return false;
87}
88
89
90
91
92
93
94static unsigned int stackleak_instrument_execute(void)
95{
96 basic_block bb, entry_bb;
97 bool prologue_instrumented = false, is_leaf = true;
98 gimple_stmt_iterator gsi;
99
100
101
102
103
104
105 gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
106 entry_bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
107
108
109
110
111
112
113 FOR_EACH_BB_FN(bb, cfun) {
114 for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
115 gimple stmt;
116
117 stmt = gsi_stmt(gsi);
118
119
120 if (is_gimple_call(stmt))
121 is_leaf = false;
122
123 if (!is_alloca(stmt))
124 continue;
125
126
127 stackleak_add_track_stack(&gsi, true);
128 if (bb == entry_bb)
129 prologue_instrumented = true;
130 }
131 }
132
133 if (prologue_instrumented)
134 return 0;
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150 if (is_leaf &&
151 !TREE_PUBLIC(current_function_decl) &&
152 DECL_DECLARED_INLINE_P(current_function_decl)) {
153 return 0;
154 }
155
156 if (is_leaf &&
157 !strncmp(IDENTIFIER_POINTER(DECL_NAME(current_function_decl)),
158 "_paravirt_", 10)) {
159 return 0;
160 }
161
162
163 bb = entry_bb;
164 if (!single_pred_p(bb)) {
165
166
167 split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
168 gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
169 bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
170 }
171 gsi = gsi_after_labels(bb);
172 stackleak_add_track_stack(&gsi, false);
173
174 return 0;
175}
176
177static bool large_stack_frame(void)
178{
179#if BUILDING_GCC_VERSION >= 8000
180 return maybe_ge(get_frame_size(), track_frame_size);
181#else
182 return (get_frame_size() >= track_frame_size);
183#endif
184}
185
186
187
188
189
190
191static unsigned int stackleak_cleanup_execute(void)
192{
193 rtx_insn *insn, *next;
194
195 if (cfun->calls_alloca)
196 return 0;
197
198 if (large_stack_frame())
199 return 0;
200
201
202
203
204
205
206
207
208
209
210
211
212 for (insn = get_insns(); insn; insn = next) {
213 rtx body;
214
215 next = NEXT_INSN(insn);
216
217
218 if (!CALL_P(insn))
219 continue;
220
221
222
223
224
225
226 body = PATTERN(insn);
227
228 if (GET_CODE(body) == PARALLEL)
229 body = XVECEXP(body, 0, 0);
230
231 if (GET_CODE(body) != CALL)
232 continue;
233
234
235
236
237
238
239 body = XEXP(body, 0);
240 if (GET_CODE(body) != MEM)
241 continue;
242
243 body = XEXP(body, 0);
244 if (GET_CODE(body) != SYMBOL_REF)
245 continue;
246
247 if (SYMBOL_REF_DECL(body) != track_function_decl)
248 continue;
249
250
251 delete_insn_and_edges(insn);
252#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION < 8000
253 if (GET_CODE(next) == NOTE &&
254 NOTE_KIND(next) == NOTE_INSN_CALL_ARG_LOCATION) {
255 insn = next;
256 next = NEXT_INSN(insn);
257 delete_insn_and_edges(insn);
258 }
259#endif
260 }
261
262 return 0;
263}
264
265static bool stackleak_gate(void)
266{
267 tree section;
268
269 section = lookup_attribute("section",
270 DECL_ATTRIBUTES(current_function_decl));
271 if (section && TREE_VALUE(section)) {
272 section = TREE_VALUE(TREE_VALUE(section));
273
274 if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10))
275 return false;
276 if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13))
277 return false;
278 if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13))
279 return false;
280 if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13))
281 return false;
282 }
283
284 return track_frame_size >= 0;
285}
286
287
288static void stackleak_start_unit(void *gcc_data __unused,
289 void *user_data __unused)
290{
291 tree fntype;
292
293
294 fntype = build_function_type_list(void_type_node, NULL_TREE);
295 track_function_decl = build_fn_decl(track_function, fntype);
296 DECL_ASSEMBLER_NAME(track_function_decl);
297 TREE_PUBLIC(track_function_decl) = 1;
298 TREE_USED(track_function_decl) = 1;
299 DECL_EXTERNAL(track_function_decl) = 1;
300 DECL_ARTIFICIAL(track_function_decl) = 1;
301 DECL_PRESERVE_P(track_function_decl) = 1;
302}
303
304
305
306
307
308
309static bool stackleak_instrument_gate(void)
310{
311 return stackleak_gate();
312}
313
314#define PASS_NAME stackleak_instrument
315#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg
316#define TODO_FLAGS_START TODO_verify_ssa | TODO_verify_flow | TODO_verify_stmts
317#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \
318 | TODO_update_ssa | TODO_rebuild_cgraph_edges
319#include "gcc-generate-gimple-pass.h"
320
321static bool stackleak_cleanup_gate(void)
322{
323 return stackleak_gate();
324}
325
326#define PASS_NAME stackleak_cleanup
327#define TODO_FLAGS_FINISH TODO_dump_func
328#include "gcc-generate-rtl-pass.h"
329
330
331
332
333
334
335__visible int plugin_init(struct plugin_name_args *plugin_info,
336 struct plugin_gcc_version *version)
337{
338 const char * const plugin_name = plugin_info->base_name;
339 const int argc = plugin_info->argc;
340 const struct plugin_argument * const argv = plugin_info->argv;
341 int i = 0;
342
343
344 static const struct ggc_root_tab gt_ggc_r_gt_stackleak[] = {
345 {
346 .base = &track_function_decl,
347 .nelt = 1,
348 .stride = sizeof(track_function_decl),
349 .cb = >_ggc_mx_tree_node,
350 .pchw = >_pch_nx_tree_node
351 },
352 LAST_GGC_ROOT_TAB
353 };
354
355
356
357
358
359
360
361
362 PASS_INFO(stackleak_instrument, "optimized", 1,
363 PASS_POS_INSERT_BEFORE);
364
365
366
367
368
369
370
371 PASS_INFO(stackleak_cleanup, "*free_cfg", 1, PASS_POS_INSERT_BEFORE);
372
373 if (!plugin_default_version_check(version, &gcc_version)) {
374 error(G_("incompatible gcc/plugin versions"));
375 return 1;
376 }
377
378
379 for (i = 0; i < argc; i++) {
380 if (!strcmp(argv[i].key, "disable"))
381 return 0;
382
383 if (!strcmp(argv[i].key, "track-min-size")) {
384 if (!argv[i].value) {
385 error(G_("no value supplied for option '-fplugin-arg-%s-%s'"),
386 plugin_name, argv[i].key);
387 return 1;
388 }
389
390 track_frame_size = atoi(argv[i].value);
391 if (track_frame_size < 0) {
392 error(G_("invalid option argument '-fplugin-arg-%s-%s=%s'"),
393 plugin_name, argv[i].key, argv[i].value);
394 return 1;
395 }
396 } else {
397 error(G_("unknown option '-fplugin-arg-%s-%s'"),
398 plugin_name, argv[i].key);
399 return 1;
400 }
401 }
402
403
404 register_callback(plugin_name, PLUGIN_INFO, NULL,
405 &stackleak_plugin_info);
406
407
408 register_callback(plugin_name, PLUGIN_START_UNIT,
409 &stackleak_start_unit, NULL);
410
411
412 register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL,
413 (void *)>_ggc_r_gt_stackleak);
414
415
416
417
418
419
420
421
422
423 register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
424 &stackleak_instrument_pass_info);
425 register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
426 &stackleak_cleanup_pass_info);
427
428 return 0;
429}
430