VirtualBox

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

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

gccplugin.cpp: Found some time to resolve the format string. Checking it is up next.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: gccplugin.cpp 56973 2015-07-17 20:35:37Z 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#define MY_LOC(a_hPreferred, a_pState) \
58 (DECL_P(a_hPreferred) ? DECL_SOURCE_LOCATION(a_hPreferred) : gimple_location((a_pState)->hStmt))
59
60
61/*******************************************************************************
62* Structures and Typedefs *
63*******************************************************************************/
64/** Checker state. */
65typedef struct MYCHECKSTATE
66{
67 gimple hStmt;
68 long iFmt;
69 long iArgs;
70} MYCHECKSTATE;
71/** Pointer to my checker state. */
72typedef MYCHECKSTATE *PMYCHECKSTATE;
73
74
75/*********************************************************************************************************************************
76* Internal Functions *
77*********************************************************************************************************************************/
78static bool MyPassGateCallback(void);
79static unsigned int MyPassExecuteCallback(void);
80static tree AttributeHandler(tree *, tree, tree, int, bool *);
81
82
83/*********************************************************************************************************************************
84* Global Variables *
85*********************************************************************************************************************************/
86/** Plug-in info. */
87static const struct plugin_info g_PlugInInfo =
88{
89 .version = "0.0.0-ALPHA",
90 .help = "Implements the __iprt_format__ attribute for checking format strings and arguments."
91};
92
93/** My pass. */
94static struct gimple_opt_pass g_MyPass =
95{
96 .pass =
97 {
98 .type = GIMPLE_PASS,
99 .name = "*iprt-format-checks", /* asterisk = no dump */
100 .optinfo_flags = 0,
101 .gate = MyPassGateCallback,
102 .execute = MyPassExecuteCallback,
103 .sub = NULL,
104 .next = NULL,
105 .static_pass_number = 0,
106 .tv_id = TV_NONE,
107 .properties_required = 0,
108 .properties_provided = 0,
109 .properties_destroyed = 0,
110 .todo_flags_start = 0,
111 .todo_flags_finish = 0,
112 }
113};
114
115/** The registration info for my pass. */
116static const struct register_pass_info g_MyPassInfo =
117{
118 .pass = &g_MyPass.pass,
119 .reference_pass_name = "ssa",
120 .ref_pass_instance_number = 1,
121 .pos_op = PASS_POS_INSERT_BEFORE,
122};
123
124
125/** Attribute specification. */
126static const struct attribute_spec g_AttribSpecs[] =
127{
128 {
129 .name = "iprt_format",
130 .min_length = 2,
131 .max_length = 2,
132 .decl_required = false,
133 .type_required = true,
134 .function_type_required = true,
135 .handler = AttributeHandler,
136 .affects_type_identity = false
137 },
138 { NULL, 0, 0, false, false, false, NULL, false }
139};
140
141
142#ifdef GCCPLUGIN_DEBUG
143
144/**
145 * Debug function for printing the scope of a decl.
146 * @param hDecl Declaration to print scope for.
147 */
148static void dprintScope(tree hDecl)
149{
150# if 0 /* later? */
151 tree hScope = CP_DECL_CONTEXT(hDecl);
152 if (hScope == global_namespace)
153 return;
154 if (TREE_CODE(hScope) == RECORD_TYPE)
155 hScope = TYPE_NAME(hScope);
156
157 /* recurse */
158 dprintScope(hScope);
159
160 /* name the scope. */
161 dprintf("::%s", DECL_NAME(hScope) ? IDENTIFIER_POINTER(DECL_NAME(hScope)) : "<noname>");
162# endif
163}
164
165
166/**
167 * Debug function for printing a declaration.
168 * @param hDecl The declaration to print.
169 */
170static void dprintDecl(tree hDecl)
171{
172 enum tree_code const enmDeclCode = TREE_CODE(hDecl);
173 tree const hType = TREE_TYPE(hDecl);
174 enum tree_code const enmTypeCode = hType ? TREE_CODE(hType) : (enum tree_code)-1;
175#if 0
176 if ( enmTypeCode == RECORD_TYPE
177 && enmDeclCode == TYPE_DECL
178 && DECL_ARTIFICIAL(hDecl))
179 dprint_class(hType);
180#endif
181
182 dprintf("%s ", tree_code_name[enmDeclCode]);
183 dprintScope(hDecl);
184 dprintf("::%s", DECL_NAME(hDecl) ? IDENTIFIER_POINTER(DECL_NAME(hDecl)) : "<noname>");
185 if (hType)
186 dprintf(" type %s", tree_code_name[enmTypeCode]);
187 dprintf(" @%s:%d", DECL_SOURCE_FILE(hDecl), DECL_SOURCE_LINE(hDecl));
188}
189
190#endif /* GCCPLUGIN_DEBUG */
191
192
193/**
194 * Gate callback for my pass that indicates whether it should execute or not.
195 * @returns true to execute.
196 */
197static bool MyPassGateCallback(void)
198{
199 dprintf("MyPassGateCallback:\n");
200 return true;
201}
202
203
204static void MyCheckFormatCString(PMYCHECKSTATE pState, const char *pszFmt, location_t hFmtLoc)
205{
206 dprintf("checker2: \"%s\"\n", pszFmt);
207}
208
209
210/**
211 * Non-recursive worker for MyCheckFormatRecursive.
212 *
213 * This will attempt to result @a hFmtArg into a string literal which it then
214 * passes on to MyCheckFormatString for the actual analyzis.
215 *
216 * @param pState The format string check state.
217 * @param hFmtArg The format string node.
218 */
219DECL_NO_INLINE(static, void) MyCheckFormatWorker(PMYCHECKSTATE pState, tree hFmtArg)
220{
221 dprintf("checker: hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
222 location_t hFmtLoc = MY_LOC(hFmtArg, pState);
223
224 /*
225 * Try resolve variables into constant strings.
226 */
227 if (VAR_P(hFmtArg))
228 {
229 hFmtArg = decl_constant_value(hFmtArg);
230 STRIP_NOPS(hFmtArg); /* Used as argument and assigned call result. */
231 dprintf("checker1: variable => hFmtArg=%p %s\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)]);
232 }
233
234 /*
235 * Fend off NULLs.
236 */
237 if (integer_zerop(hFmtArg))
238 warning_at(hFmtLoc, 0, "Format string should not be NULL");
239 /*
240 * Need address expression to get any further.
241 */
242 else if (TREE_CODE(hFmtArg) != ADDR_EXPR)
243 dprintf("checker1: Not address expression (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
244 else
245 {
246 hFmtLoc = EXPR_LOC_OR_LOC(hFmtArg, hFmtLoc);
247 hFmtArg = TREE_OPERAND(hFmtArg, 0);
248
249 /*
250 * Deal with fixed string indexing, if possible.
251 */
252 HOST_WIDE_INT off = 0;
253 if ( TREE_CODE(hFmtArg) == ARRAY_REF
254 && TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).fits_shwi() )
255 {
256 off = TREE_INT_CST(TREE_OPERAND(hFmtArg, 1)).to_shwi();
257 if (off < 0)
258 {
259 dprintf("checker1: ARRAY_REF, off=%ld\n", off);
260 return;
261 }
262 hFmtArg = TREE_OPERAND(hFmtArg, 0);
263 dprintf("checker1: ARRAY_REF => hFmtArg=%p %s, off=%ld\n", hFmtArg, tree_code_name[TREE_CODE(hFmtArg)], off);
264 }
265
266 /*
267 * Deal with static const char g_szFmt[] = "qwerty"; Take care as
268 * the actual string constant may not necessarily include the terminator.
269 */
270 tree hArraySize = NULL_TREE;
271 if ( VAR_P(hFmtArg)
272 && TREE_CODE(TREE_TYPE(hFmtArg)) == ARRAY_TYPE)
273 {
274 tree hArrayInitializer = decl_constant_value(hFmtArg);
275 if ( hArrayInitializer != hFmtArg
276 && TREE_CODE(hArrayInitializer) == STRING_CST)
277 {
278 hArraySize = DECL_SIZE_UNIT(hFmtArg);
279 hFmtArg = hArrayInitializer;
280 }
281 }
282
283 /*
284 * Are we dealing with a string literal now?
285 */
286 if (TREE_CODE(hFmtArg) != STRING_CST)
287 dprintf("checker1: Not string literal (%s)\n", tree_code_name[TREE_CODE(hFmtArg)]);
288 else if (TYPE_MAIN_VARIANT(TREE_TYPE(TREE_TYPE(hFmtArg))) != char_type_node)
289 warning_at(hFmtLoc, 0, "expected 'char' type string literal");
290 else
291 {
292 /*
293 * Yes we are, so get the pointer to the string and its length.
294 */
295 const char *pszFmt = TREE_STRING_POINTER(hFmtArg);
296 int cchFmt = TREE_STRING_LENGTH(hFmtArg);
297
298 /* Adjust cchFmt to the initialized array size if appropriate. */
299 if (hArraySize != NULL_TREE)
300 {
301 if (TREE_CODE(hArraySize) != INTEGER_CST)
302 warning_at(hFmtLoc, 0, "Expected integer array size (not %s)", tree_code_name[TREE_CODE(hArraySize)]);
303 else if (!TREE_INT_CST(hArraySize).fits_shwi())
304 warning_at(hFmtLoc, 0, "Unexpected integer overflow in array size constant");
305 else
306 {
307 HOST_WIDE_INT cbArray = TREE_INT_CST(hArraySize).to_shwi();
308 if ( cbArray <= 0
309 || cbArray != (int)cbArray)
310 warning_at(hFmtLoc, 0, "Unexpected integer array size constant value: %ld", cbArray);
311 else if (cchFmt > cbArray)
312 {
313 dprintf("checker1: cchFmt=%d => cchFmt=%ld (=cbArray)\n", cchFmt, cbArray);
314 cchFmt = (int)cbArray;
315 }
316 }
317 }
318
319 /* Apply the offset, if given. */
320 if (off)
321 {
322 if (off >= cchFmt)
323 {
324 dprintf("checker1: off=%ld >= cchFmt=%d -> skipping\n", off, cchFmt);
325 return;
326 }
327 pszFmt += off;
328 cchFmt -= (int)off;
329 }
330
331 /*
332 * Check for unterminated strings.
333 */
334 if ( cchFmt < 1
335 || pszFmt[cchFmt - 1] != '\0')
336 warning_at(hFmtLoc, 0, "Unterminated format string (cchFmt=%d)", cchFmt);
337 /*
338 * Call worker to check the actual string.
339 */
340 else
341 MyCheckFormatCString(pState, pszFmt, hFmtLoc);
342 }
343 }
344}
345
346
347/**
348 * Deal recursively with special format string constructs.
349 *
350 * This will call MyCheckFormatWorker to validate each format string.
351 *
352 * @param pState The format string check state.
353 * @param hFmtArg The format string node.
354 */
355static void MyCheckFormatRecursive(PMYCHECKSTATE pState, tree hFmtArg)
356{
357 /*
358 * NULL format strings may cause crashes.
359 */
360 if (integer_zerop(hFmtArg))
361 warning_at(MY_LOC(hFmtArg, pState), 0, "Format string should not be NULL");
362 /*
363 * Check both branches of a ternary operator.
364 */
365 else if (TREE_CODE(hFmtArg) == COND_EXPR)
366 {
367 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 1));
368 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 2));
369 }
370 /*
371 * Strip coercion.
372 */
373 else if ( CONVERT_EXPR_P(hFmtArg)
374 && TYPE_PRECISION(TREE_TYPE(hFmtArg)) == TYPE_PRECISION(TREE_TYPE(TREE_OPERAND(hFmtArg, 0))) )
375 MyCheckFormatRecursive(pState, TREE_OPERAND(hFmtArg, 0));
376 /*
377 * We're good, hand it to the non-recursive worker.
378 */
379 else
380 MyCheckFormatWorker(pState, hFmtArg);
381}
382
383
384/**
385 * Execute my pass.
386 * @returns Flags indicates stuff todo, we return 0.
387 */
388static unsigned int MyPassExecuteCallback(void)
389{
390 dprintf("MyPassExecuteCallback:\n");
391
392 /*
393 * Enumerate the basic blocks.
394 */
395 basic_block hBasicBlock;
396 FOR_EACH_BB(hBasicBlock)
397 {
398 dprintf(" hBasicBlock=%p\n", hBasicBlock);
399
400 /*
401 * Enumerate the statements in the current basic block.
402 * We're interested in calls to functions with the __iprt_format__ attribute.
403 */
404 for (gimple_stmt_iterator hStmtItr = gsi_start_bb(hBasicBlock); !gsi_end_p(hStmtItr); gsi_next(&hStmtItr))
405 {
406 gimple const hStmt = gsi_stmt(hStmtItr);
407 enum gimple_code const enmCode = gimple_code(hStmt);
408#ifdef GCCPLUGIN_DEBUG
409 unsigned const cOps = gimple_num_ops(hStmt);
410 dprintf(" hStmt=%p %s (%d) ops=%d\n", hStmt, gimple_code_name[enmCode], enmCode, cOps);
411 for (unsigned iOp = 0; iOp < cOps; iOp++)
412 {
413 tree const hOp = gimple_op(hStmt, iOp);
414 if (hOp)
415 dprintf(" %02d: %p, code %s(%d)\n", iOp, hOp, tree_code_name[TREE_CODE(hOp)], TREE_CODE(hOp));
416 else
417 dprintf(" %02d: NULL_TREE\n", iOp);
418 }
419#endif
420 if (enmCode == GIMPLE_CALL)
421 {
422 /*
423 * Check if the function type has the __iprt_format__ attribute.
424 */
425 tree const hFn = gimple_call_fn(hStmt);
426 tree const hFnType = gimple_call_fntype(hStmt);
427 tree const hAttr = lookup_attribute("iprt_format", TYPE_ATTRIBUTES(hFnType));
428#ifdef GCCPLUGIN_DEBUG
429 tree const hFnDecl = gimple_call_fndecl(hStmt);
430 dprintf(" hFn =%p %s(%d); args=%d\n",
431 hFn, tree_code_name[TREE_CODE(hFn)], TREE_CODE(hFn), gimple_call_num_args(hStmt));
432 if (hFnDecl)
433 dprintf(" hFnDecl=%p %s(%d) type=%p %s:%d\n", hFnDecl, tree_code_name[TREE_CODE(hFnDecl)],
434 TREE_CODE(hFnDecl), TREE_TYPE(hFnDecl), DECL_SOURCE_FILE(hFnDecl), DECL_SOURCE_LINE(hFnDecl));
435 if (hFnType)
436 dprintf(" hFnType=%p %s(%d)\n", hFnType, tree_code_name[TREE_CODE(hFnType)], TREE_CODE(hFnType));
437#endif
438 if (hAttr)
439 {
440 /*
441 * Yeah, it has the attribute!
442 */
443 tree const hAttrArgs = TREE_VALUE(hAttr);
444 MYCHECKSTATE State;
445 State.iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
446 State.iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
447 State.hStmt = hStmt;
448 dprintf(" %s() __iprt_format__(iFmt=%ld, iArgs=%ld)\n",
449 DECL_NAME(hFnDecl) ? IDENTIFIER_POINTER(DECL_NAME(hFnDecl)) : "<unamed>", State.iFmt, State.iArgs);
450
451 MyCheckFormatRecursive(&State, gimple_call_arg(hStmt, State.iFmt - 1));
452 }
453 }
454 }
455 }
456 return 0;
457}
458
459
460
461/**
462 * Validate the use of an attribute.
463 *
464 * @returns ??
465 * @param phOnNode The node the attribute is being used on.
466 * @param hAttrName The attribute name.
467 * @param hAttrArgs The attribute arguments.
468 * @param fFlags Some kind of flags...
469 * @param pfDontAddAttrib Whether to add the attribute to this node or not.
470 */
471static tree AttributeHandler(tree *phOnNode, tree hAttrName, tree hAttrArgs, int fFlags, bool *pfDontAddAttrib)
472{
473 dprintf("AttributeHandler: name=%s fFlags=%#x", IDENTIFIER_POINTER(hAttrName), fFlags);
474 long iFmt = TREE_INT_CST(TREE_VALUE(hAttrArgs)).to_shwi();
475 long iArgs = TREE_INT_CST(TREE_VALUE(TREE_CHAIN(hAttrArgs))).to_shwi();
476 dprintf(" iFmt=%ld iArgs=%ld", iFmt, iArgs);
477
478 tree hType = *phOnNode;
479 dprintf(" hType=%p %s(%d)\n", hType, tree_code_name[TREE_CODE(hType)], TREE_CODE(hType));
480
481 if (pfDontAddAttrib)
482 *pfDontAddAttrib = false;
483 return NULL_TREE;
484}
485
486
487/**
488 * Called when we can register attributes.
489 *
490 * @param pvEventData Ignored.
491 * @param pvUser Ignored.
492 */
493static void RegisterAttributesEvent(void *pvEventData, void *pvUser)
494{
495 NOREF(pvEventData); NOREF(pvUser);
496 dprintf("RegisterAttributesEvent: pvEventData=%p\n", pvEventData);
497
498 register_attribute(&g_AttribSpecs[0]);
499}
500
501
502/**
503 * The plug-in entry point.
504 *
505 * @returns 0 to indicate success?
506 * @param pPlugInInfo Plugin info structure.
507 * @param pGccVer GCC Version.
508 */
509int plugin_init(plugin_name_args *pPlugInInfo, plugin_gcc_version *pGccVer)
510{
511 dprintf("plugin_init: %s\n", pPlugInInfo->full_name);
512 dprintf("gcc version: basever=%s datestamp=%s devphase=%s revision=%s\n",
513 pGccVer->basever, pGccVer->datestamp, pGccVer->devphase, pGccVer->revision);
514
515 /* Ask for callback in which we may register the attribute. */
516 register_callback(pPlugInInfo->base_name, PLUGIN_ATTRIBUTES, RegisterAttributesEvent, NULL /*pvUser*/);
517
518 /* Register our pass. */
519 register_callback(pPlugInInfo->base_name, PLUGIN_PASS_MANAGER_SETUP, NULL, (void *)&g_MyPassInfo);
520
521 /* Register plug-in info. */
522 register_callback(pPlugInInfo->base_name, PLUGIN_INFO, NULL, (void *)&g_PlugInInfo);
523
524 return 0;
525}
526
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