VirtualBox

source: vbox/trunk/src/bldprogs/gccplugin.cpp@ 56975

Last change on this file since 56975 was 56975, checked in by vboxsync, 10 years ago

gccplugin.cpp: a wee bit further.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/* $Id: gccplugin.cpp 56975 2015-07-18 00:18:26Z vboxsync $ */
2/** @file
3 * gccplugin - GCC plugin for checking IPRT format strings.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*********************************************************************************************************************************
19* Header Files *
20*********************************************************************************************************************************/
21#include <stdio.h>
22#include <iprt/cdefs.h>
23
24#include "plugin.h"
25#include "basic-block.h"
26#include "gimple.h"
27#include "tree.h"
28#include "tree-pass.h"
29#include "cp/cp-tree.h"
30
31
32/*********************************************************************************************************************************
33* Global Variables *
34*********************************************************************************************************************************/
35/** License indicator. */
36int plugin_is_GPL_compatible;
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42#define GCCPLUGIN_DEBUG
43/** Debug printf. */
44#ifdef GCCPLUGIN_DEBUG
45# define dprintf(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
46#else
47# define dprintf(...) do { } while (0)
48#endif
49
50/** Convencience macro not present in earlier gcc versions. */
51#ifndef VAR_P
52# define VAR_P(a_hNode) (TREE_CODE(a_hNode) == VAR_DECL)
53#endif
54
55
56/** For use with messages.
57 * @todo needs some more work... Actually, seems we're a bit handicapped by
58 * working on gimplified stuff. */
59#define MY_LOC(a_hPreferred, a_pState) EXPR_LOC_OR_LOC(a_hPreferred, (a_pState)->hFmtLoc)
60
61
62/*******************************************************************************
63* Structures and Typedefs *
64*******************************************************************************/
65/** Checker state. */
66typedef struct MYCHECKSTATE
67{
68 gimple hStmt;
69 long iFmt;
70 long iArgs;
71 location_t hFmtLoc;
72} MYCHECKSTATE;
73/** Pointer to my checker state. */
74typedef MYCHECKSTATE *PMYCHECKSTATE;
75
76
77/*********************************************************************************************************************************
78* Internal Functions *
79*********************************************************************************************************************************/
80static bool MyPassGateCallback(void);
81static unsigned int MyPassExecuteCallback(void);
82static tree AttributeHandler(tree *, tree, tree, int, bool *);
83
84
85/*********************************************************************************************************************************
86* Global Variables *
87*********************************************************************************************************************************/
88/** Plug-in info. */
89static const struct plugin_info g_PlugInInfo =
90{
91 .version = "0.0.0-ALPHA",
92 .help = "Implements the __iprt_format__ attribute for checking format strings and arguments."
93};
94
95/** My pass. */
96static struct gimple_opt_pass g_MyPass =
97{
98 .pass =
99 {
100 .type = GIMPLE_PASS,
101 .name = "*iprt-format-checks", /* asterisk = no dump */
102 .optinfo_flags = 0,
103 .gate = MyPassGateCallback,
104 .execute = MyPassExecuteCallback,
105 .sub = NULL,
106 .next = NULL,
107 .static_pass_number = 0,
108 .tv_id = TV_NONE,
109 .properties_required = 0,
110 .properties_provided = 0,
111 .properties_destroyed = 0,
112 .todo_flags_start = 0,
113 .todo_flags_finish = 0,
114 }
115};
116
117/** The registration info for my pass. */
118static const struct register_pass_info g_MyPassInfo =
119{
120 .pass = &g_MyPass.pass,
121 .reference_pass_name = "ssa",
122 .ref_pass_instance_number = 1,
123 .pos_op = PASS_POS_INSERT_BEFORE,
124};
125
126
127/** Attribute specification. */
128static const struct attribute_spec g_AttribSpecs[] =
129{
130 {
131 .name = "iprt_format",
132 .min_length = 2,
133 .max_length = 2,
134 .decl_required = false,
135 .type_required = true,
136 .function_type_required = true,
137 .handler = AttributeHandler,
138 .affects_type_identity = false
139 },
140 { NULL, 0, 0, false, false, false, NULL, false }
141};
142
143
144#ifdef GCCPLUGIN_DEBUG
145
146/**
147 * Debug function for printing the scope of a decl.
148 * @param hDecl Declaration to print scope for.
149 */
150static void dprintScope(tree hDecl)
151{
152# if 0 /* later? */
153 tree hScope = CP_DECL_CONTEXT(hDecl);
154 if (hScope == global_namespace)
155 return;
156 if (TREE_CODE(hScope) == RECORD_TYPE)
157 hScope = TYPE_NAME(hScope);
158
159 /* recurse */
160 dprintScope(hScope);
161
162 /* name the scope. */
163 dprintf("::%s", DECL_NAME(hScope) ? IDENTIFIER_POINTER(DECL_NAME(hScope)) : "<noname>");
164# endif
165}
166
167
168/**
169 * Debug function for printing a declaration.
170 * @param hDecl The declaration to print.
171 */
172static void dprintDecl(tree hDecl)
173{
174 enum tree_code const enmDeclCode = TREE_CODE(hDecl);
175 tree const hType = TREE_TYPE(hDecl);
176 enum tree_code const enmTypeCode = hType ? TREE_CODE(hType) : (enum tree_code)-1;
177#if 0
178 if ( enmTypeCode == RECORD_TYPE
179 && enmDeclCode == TYPE_DECL
180 && DECL_ARTIFICIAL(hDecl))
181 dprint_class(hType);
182#endif
183
184 dprintf("%s ", tree_code_name[enmDeclCode]);
185 dprintScope(hDecl);
186 dprintf("::%s", DECL_NAME(hDecl) ? IDENTIFIER_POINTER(DECL_NAME(hDecl)) : "<noname>");
187 if (hType)
188 dprintf(" type %s", tree_code_name[enmTypeCode]);
189 dprintf(" @%s:%d", DECL_SOURCE_FILE(hDecl), DECL_SOURCE_LINE(hDecl));
190}
191
192#endif /* GCCPLUGIN_DEBUG */
193
194
195/**
196 * Gate callback for my pass that indicates whether it should execute or not.
197 * @returns true to execute.
198 */
199static bool MyPassGateCallback(void)
200{
201 dprintf("MyPassGateCallback:\n");
202 return true;
203}
204
205
206#define MYSTATE_FMT_FILE(a_pState) LOCATION_FILE((a_pState)->hFmtLoc)
207#define MYSTATE_FMT_LINE(a_pState) LOCATION_LINE((a_pState)->hFmtLoc)
208#define MYSTATE_FMT_COLUMN(a_pState) LOCATION_COLUMN((a_pState)->hFmtLoc)
209
210
211/**
212 * Checks that @a iFmtArg isn't present or a valid final dummy argument.
213 *
214 * Will issue warning/error if there are more arguments at @a iFmtArg.
215 *
216 * @param pState The format string checking state.
217 * @param iArg The index of the end of arguments, this is
218 * relative to MYCHECKSTATE::iArgs.
219 */
220void MyCheckFinalArg(PMYCHECKSTATE pState, unsigned iArg)
221{
222 dprintf("MyCheckFinalArg: iArg=%u iArgs=%ld cArgs=%u\n", iArg, pState->iArgs, gimple_call_num_args(pState->hStmt));
223 if (pState->iArgs > 0)
224 {
225 iArg += pState->iArgs - 1;
226 unsigned cArgs = gimple_call_num_args(pState->hStmt);
227 if (iArg == cArgs)
228 { /* fine */ }
229 else if (iArg < cArgs)
230 {
231 tree hArg = gimple_call_arg(pState->hStmt, iArg);
232 if (cArgs - iArg > 1)
233 warning_at(MY_LOC(hArg, pState), 0, "%u extra arguments not consumed by format string", cArgs - iArg);
234 else if ( TREE_CODE(hArg) != INTEGER_CST
235 || TREE_INT_CST(hArg).fits_shwi()
236 || TREE_INT_CST(hArg).to_shwi() != -99) /* ignore final dummy argument: ..., -99); */
237 warning_at(MY_LOC(hArg, pState), 0, "one extra argument not consumed by format string");
238 }
239 /* This should be handled elsewhere, but just in case. */
240 else if (iArg - 1 == cArgs)
241 warning_at(pState->hFmtLoc, 0, "one argument too few");
242 else
243 warning_at(pState->hFmtLoc, 0, "%u arguments too few", iArg - cArgs);
244 }
245}
246
247
248/**
249 * Does the actual format string checking.
250 *
251 * @todo Move this to different file common to both GCC and CLANG later.
252 *
253 * @param pState The format string checking state.
254 * @param pszFmt The format string.
255 */
256void MyCheckFormatCString(PMYCHECKSTATE pState, const char *pszFmt)
257{
258 dprintf("checker2: \"%s\" at %s:%d col %d\n", pszFmt,
259 MYSTATE_FMT_FILE(pState), MYSTATE_FMT_LINE(pState), MYSTATE_FMT_COLUMN(pState));
260 unsigned iArg = 0;
261 for (;;)
262 {
263 /*
264 * Skip to the next argument.
265 */
266 char ch;
267 while ((ch = *pszFmt++) != '%')
268 if (ch == '\0')
269 {
270 MyCheckFinalArg(pState, iArg);
271 return;
272 }
273
274
275 }
276}
277
278
279/**
280 * Non-recursive worker for MyCheckFormatRecursive.
281 *
282 * This will attempt to result @a hFmtArg into a string literal which it then
283 * passes on to MyCheckFormatString for the actual analyzis.
284 *
285 * @param pState The format string checking state.
286 * @param hFmtArg The format string node.
287 */
288DECL_NO_INLINE(static, void) MyCheckFormatNonRecursive(PMYCHECKSTATE pState, tree hFmtArg)
289{
290 dprintf("checker: hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
291
292 /*
293 * Try resolve variables into constant strings.
294 */
295 if (VAR_P(hFmtArg))
296 {
297 hFmtArg = decl_constant_value(hFmtArg);
298 STRIP_NOPS(hFmtArg); /* Used as argument and assigned call result. */
299 dprintf("checker1: variable => hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
300 }
301
302 /*
303 * Fend off NULLs.
304 */
305 if (integer_zerop(hFmtArg))
306 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
307 /*
308 * Need address expression to get any further.
309 */
310 else if (TREE_CODE(hFmtArg) != ADDR_EXPR)
311 dprintf("checker1: Not address expression (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
312 else
313 {
314 pState->hFmtLoc = EXPR_LOC_OR_LOC(hFmtArg, pState->hFmtLoc);
315 hFmtArg = TREE_OPERAND(hFmtArg, 0);
316
317 /*
318 * Deal with fixed string indexing, if possible.
319 */
320 HOST_WIDE_INT off = 0;
321 if ( TREE_CODE(hFmtArg) == ARRAY_REF
322 && TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).fits_shwi() )
323 {
324 off = TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).to_shwi();
325 if (off < 0)
326 {
327 dprintf("checker1: ARRAY_REF, off=%ld\n", off);
328 return;
329 }
330 hFmtArg = TREE_OPERAND(hFmtArg, 0);
331 dprintf("checker1: ARRAY_REF => hFmtArg=%p %s, off=%ld\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)], off);
332 }
333
334 /*
335 * Deal with static const char g_szFmt[] = "qwerty"; Take care as
336 * the actual string constant may not necessarily include the terminator.
337 */
338 tree hArraySize = NULL_TREE;
339 if ( VAR_P(hFmtArg)
340 && TREE_CODE(TREE_TYPE(hFmtArg)) == ARRAY_TYPE)
341 {
342 tree hArrayInitializer = decl_constant_value(hFmtArg);
343 if ( hArrayInitializer != hFmtArg
344 && TREE_CODE(hArrayInitializer) == STRING_CST)
345 {
346 hArraySize = DECL_SIZE_UNIT(hFmtArg);
347 hFmtArg = hArrayInitializer;
348 }
349 }
350
351 /*
352 * Are we dealing with a string literal now?
353 */
354 if (TREE_CODE(hFmtArg) != STRING_CST)
355 dprintf("checker1: Not string literal (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
356 else if (TYPE_MAIN_VARIANT(TREE_TYPE(TREE_TYPE(hFmtArg))) != char_type_node)
357 warning_at(pState->hFmtLoc, 0, "expected 'char' type string literal");
358 else
359 {
360 /*
361 * Yes we are, so get the pointer to the string and its length.
362 */
363 const char *pszFmt = TREE_STRING_POINTER(hFmtArg);
364 int cchFmt = TREE_STRING_LENGTH(hFmtArg);
365
366 /* Adjust cchFmt to the initialized array size if appropriate. */
367 if (hArraySize != NULL_TREE)
368 {
369 if (TREE_CODE(hArraySize) != INTEGER_CST)
370 warning_at(pState->hFmtLoc, 0, "Expected integer array size (not %s)", tree_code_name[TREE_CODE(hArraySize)]);
371 else if (!TREE_INT_CST(hArraySize).fits_shwi())
372 warning_at(pState->hFmtLoc, 0, "Unexpected integer overflow in array size constant");
373 else
374 {
375 HOST_WIDE_INT cbArray = TREE_INT_CST(hArraySize).to_shwi();
376 if ( cbArray <= 0
377 || cbArray != (int)cbArray)
378 warning_at(pState->hFmtLoc, 0, "Unexpected integer array size constant value: %ld", cbArray);
379 else if (cchFmt > cbArray)
380 {
381 dprintf("checker1: cchFmt=%d => cchFmt=%ld (=cbArray)\n", cchFmt, cbArray);
382 cchFmt = (int)cbArray;
383 }
384 }
385 }
386
387 /* Apply the offset, if given. */
388 if (off)
389 {
390 if (off >= cchFmt)
391 {
392 dprintf("checker1: off=%ld >= cchFmt=%d -> skipping\n", off, cchFmt);
393 return;
394 }
395 pszFmt += off;
396 cchFmt -= (int)off;
397 }
398
399 /*
400 * Check for unterminated strings.
401 */
402 if ( cchFmt < 1
403 || pszFmt[cchFmt - 1] != '\0')
404 warning_at(pState->hFmtLoc, 0, "Unterminated format string (cchFmt=%d)", cchFmt);
405 /*
406 * Call worker to check the actual string.
407 */
408 else
409 MyCheckFormatCString(pState, pszFmt);
410 }
411 }
412}
413
414
415/**
416 * Deal recursively with special format string constructs.
417 *
418 * This will call MyCheckFormatNonRecursive to validate each format string.
419 *
420 * @param pState The format string checking state.
421 * @param hFmtArg The format string node.
422 */
423static void MyCheckFormatRecursive(PMYCHECKSTATE pState, tree hFmtArg)
424{
425 /*
426 * NULL format strings may cause crashes.
427 */
428 if (integer_zerop(hFmtArg))
429 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
430 /*
431 * Check both branches of a ternary operator.
432 */
433 else if (TREE_CODE(hFmtArg) == COND_EXPR)
434 {
435 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 1));
436 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 2));
437 }
438 /*
439 * Strip coercion.
440 */
441 else if ( CONVERT_EXPR_P(hFmtArg)
442 && TYPE_PRECISION(TREE_TYPE(hFmtArg)) == TYPE_PRECISION(TREE_TYPE(TREE_OPERAND(hFmtArg, 0))) )
443 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 0));
444 /*
445 * We're good, hand it to the non-recursive worker.
446 */
447 else
448 MyCheckFormatNonRecursive(pState, hFmtArg);
449}
450
451
452/**
453 * Execute my pass.
454 * @returns Flags indicates stuff todo, we return 0.
455 */
456static unsigned int MyPassExecuteCallback(void)
457{
458 dprintf("MyPassExecuteCallback:\n");
459
460 /*
461 * Enumerate the basic blocks.
462 */
463 basic_block hBasicBlock;
464 FOR_EACH_BB(hBasicBlock)
465 {
466 dprintf(" hBasicBlock=%p\n", hBasicBlock);
467
468 /*
469 * Enumerate the statements in the current basic block.
470 * We're interested in calls to functions with the __iprt_format__ attribute.
471 */
472 for (gimple_stmt_iterator hStmtItr = gsi_start_bb(hBasicBlock); !gsi_end_p(hStmtItr); gsi_next(&hStmtItr))
473 {
474 gimple const hStmt = gsi_stmt(hStmtItr);
475 enum gimple_code const enmCode = gimple_code(hStmt);
476#ifdef GCCPLUGIN_DEBUG
477 unsigned const cOps = gimple_num_ops(hStmt);
478 dprintf(" hStmt=%p %s (%d) ops=%d\n", hStmt, gimple_code_name[enmCode], enmCode, cOps);
479 for (unsigned iOp = 0; iOp < cOps; iOp++)
480 {
481 tree const hOp = gimple_op(hStmt, iOp);
482 if (hOp)
483 dprintf(" %02d: %p, code %s(%d)\n", iOp, hOp, tree_code_name[TREE_CODE(hOp)], TREE_CODE(hOp));
484 else
485 dprintf(" %02d: NULL_TREE\n", iOp);
486 }
487#endif
488 if (enmCode == GIMPLE_CALL)
489 {
490 /*
491 * Check if the function type has the __iprt_format__ attribute.
492 */
493 tree const hFn = gimple_call_fn(hStmt);
494 tree const hFnType = gimple_call_fntype(hStmt);
495 tree const hAttr = lookup_attribute("iprt_format", TYPE_ATTRIBUTES(hFnType));
496#ifdef GCCPLUGIN_DEBUG
497 tree const hFnDecl = gimple_call_fndecl(hStmt);
498 dprintf(" hFn =%p %s(%d); args=%d\n",
499 hFn, tree_code_name[TREE_CODE(hFn)], TREE_CODE(hFn), gimple_call_num_args(hStmt));
500 if (hFnDecl)
501 dprintf(" hFnDecl=%p %s(%d) type=%p %s:%d\n", hFnDecl, tree_code_name[TREE_CODE(hFnDecl)],
502 TREE_CODE(hFnDecl), TREE_TYPE(hFnDecl), DECL_SOURCE_FILE(hFnDecl), DECL_SOURCE_LINE(hFnDecl));
503 if (hFnType)
504 dprintf(" hFnType=%p %s(%d)\n", hFnType, tree_code_name[TREE_CODE(hFnType)], TREE_CODE(hFnType));
505#endif
506 if (hAttr)
507 {
508 /*
509 * Yeah, it has the attribute!
510 */
511 tree const hAttrArgs = TREE_VALUE(hAttr);
512 MYCHECKSTATE State;
513 State.iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
514 State.iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
515 State.hStmt = hStmt;
516 State.hFmtLoc = gimple_location(hStmt);
517 dprintf(" %s() __iprt_format__(iFmt=%ld, iArgs=%ld)\n",
518 DECL_NAME(hFnDecl) ? IDENTIFIER_POINTER(DECL_NAME(hFnDecl)) : "<unamed>", State.iFmt, State.iArgs);
519
520 MyCheckFormatRecursive(&State, gimple_call_arg(hStmt, State.iFmt - 1));
521 }
522 }
523 }
524 }
525 return 0;
526}
527
528
529
530/**
531 * Validate the use of an attribute.
532 *
533 * @returns ??
534 * @param phOnNode The node the attribute is being used on.
535 * @param hAttrName The attribute name.
536 * @param hAttrArgs The attribute arguments.
537 * @param fFlags Some kind of flags...
538 * @param pfDontAddAttrib Whether to add the attribute to this node or not.
539 */
540static tree AttributeHandler(tree *phOnNode, tree hAttrName, tree hAttrArgs, int fFlags, bool *pfDontAddAttrib)
541{
542 dprintf("AttributeHandler: name=%s fFlags=%#x", IDENTIFIER_POINTER(hAttrName), fFlags);
543 long iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
544 long iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
545 dprintf(" iFmt=%ld iArgs=%ld", iFmt, iArgs);
546
547 tree hType = *phOnNode;
548 dprintf(" hType=%p %s(%d)\n", hType, tree_code_name[TREE_CODE(hType)], TREE_CODE(hType));
549
550 if (pfDontAddAttrib)
551 *pfDontAddAttrib = false;
552 return NULL_TREE;
553}
554
555
556/**
557 * Called when we can register attributes.
558 *
559 * @param pvEventData Ignored.
560 * @param pvUser Ignored.
561 */
562static void RegisterAttributesEvent(void *pvEventData, void *pvUser)
563{
564 NOREF(pvEventData); NOREF(pvUser);
565 dprintf("RegisterAttributesEvent: pvEventData=%p\n", pvEventData);
566
567 register_attribute(&g_AttribSpecs[0]);
568}
569
570
571/**
572 * The plug-in entry point.
573 *
574 * @returns 0 to indicate success?
575 * @param pPlugInInfo Plugin info structure.
576 * @param pGccVer GCC Version.
577 */
578int plugin_init(plugin_name_args *pPlugInInfo, plugin_gcc_version *pGccVer)
579{
580 dprintf("plugin_init: %s\n", pPlugInInfo->full_name);
581 dprintf("gcc version: basever=%s datestamp=%s devphase=%s revision=%s\n",
582 pGccVer->basever, pGccVer->datestamp, pGccVer->devphase, pGccVer->revision);
583
584 /* Ask for callback in which we may register the attribute. */
585 register_callback(pPlugInInfo->base_name, PLUGIN_ATTRIBUTES, RegisterAttributesEvent, NULL /*pvUser*/);
586
587 /* Register our pass. */
588 register_callback(pPlugInInfo->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, (void *)&g_MyPassInfo);
589
590 /* Register plug-in info. */
591 register_callback(pPlugInInfo->base_name, PLUGIN_INFO, NULL, (void *)&g_PlugInInfo);
592
593 return 0;
594}
595
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette