Skip to content

Commit 3bb8b61

Browse files
committed
Preprocessor5: add Rule PRE32-C
1 parent 2b0527b commit 3bb8b61

File tree

11 files changed

+194
-26
lines changed

11 files changed

+194
-26
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# PRE32-C: Do not use preprocessor directives in invocations of function-like macros
2+
3+
This query implements the CERT-C rule PRE32-C:
4+
5+
> Do not use preprocessor directives in invocations of function-like macros
6+
7+
## CERT
8+
9+
** REPLACE THIS BY RUNNING THE SCRIPT `scripts/help/cert-help-extraction.py` **
10+
11+
## Implementation notes
12+
13+
None
14+
15+
## References
16+
17+
* CERT-C: [PRE32-C: Do not use preprocessor directives in invocations of function-like macros](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* @id c/cert/macro-or-function-args-contain-hash-token
3+
* @name PRE32-C: Do not use preprocessor directives in invocations of function-like macros
4+
* @description Arguments to a function-like macros shall not contain tokens that look like
5+
* pre-processing directives or else behaviour after macro expansion is unpredictable.
6+
* This rule applies to functions as well in case they are implemented using macros.
7+
* @kind problem
8+
* @precision high
9+
* @problem.severity warning
10+
* @tags external/cert/id/pre32-c
11+
* readability
12+
* correctness
13+
* external/cert/obligation/rule
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.cert
18+
import codingstandards.cpp.PreprocessorDirective
19+
20+
pragma[noinline]
21+
predicate isFunctionInvocationLocation(FunctionCall call, File f, int startline, int endline) {
22+
call.getLocation().hasLocationInfo(f.getAbsolutePath(), startline, _, _, _) and
23+
//for all function calls the closest location heurisitc we have for end line is the next node in cfg
24+
call.getASuccessor().getLocation().hasLocationInfo(f.getAbsolutePath(), endline, _, _, _)
25+
}
26+
27+
PreprocessorDirective isLocatedInAFunctionInvocation(FunctionCall c) {
28+
exists(PreprocessorDirective p, File f, int startCall, int endCall |
29+
isFunctionInvocationLocation(c, f, startCall, endCall) and
30+
exists(int startLine, int endLine | isPreprocDirectiveLine(p, f, startLine, endLine) |
31+
startCall < startLine and
32+
startCall < endLine and
33+
endLine <= endCall and
34+
endLine <= endCall
35+
) and
36+
result = p
37+
)
38+
}
39+
40+
from PreprocessorDirective p, string msg
41+
where
42+
not isExcluded(p, Preprocessor5Package::macroOrFunctionArgsContainHashTokenQuery()) and
43+
exists(FunctionCall c |
44+
p = isLocatedInAFunctionInvocation(c) and
45+
//if this is actually a function in a macro ignore it because it will still be seen in the macro condition
46+
not c.isInMacroExpansion() and
47+
msg =
48+
"Invocation of function " + c.getTarget().getName() + " includes a token \"" + p +
49+
"\" that could be confused for an argument preprocessor directive."
50+
)
51+
or
52+
exists(MacroInvocation m |
53+
p = isLocatedInAMacroInvocation(m) and
54+
msg =
55+
"Invocation of macro " + m.getMacroName() + " includes a token \"" + p +
56+
"\" that could be confused for an argument preprocessor directive."
57+
)
58+
select p, msg
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
| test.c:9:1:9:19 | #if NOTDEFINEDMACRO | Invocation of macro MACROFUNCTION includes a token "#if NOTDEFINEDMACRO" that could be confused for an argument preprocessor directive. |
2+
| test.c:11:1:11:5 | #else | Invocation of macro MACROFUNCTION includes a token "#else" that could be confused for an argument preprocessor directive. |
3+
| test.c:13:1:13:6 | #endif | Invocation of macro MACROFUNCTION includes a token "#endif" that could be confused for an argument preprocessor directive. |
4+
| test.c:20:1:20:16 | #ifdef SOMEMACRO | Invocation of function memcpy includes a token "#ifdef SOMEMACRO" that could be confused for an argument preprocessor directive. |
5+
| test.c:22:1:22:5 | #else | Invocation of function memcpy includes a token "#else" that could be confused for an argument preprocessor directive. |
6+
| test.c:24:1:24:6 | #endif | Invocation of function memcpy includes a token "#endif" that could be confused for an argument preprocessor directive. |
7+
| test.c:27:1:27:8 | #if TEST | Invocation of function memcpy includes a token "#if TEST" that could be confused for an argument preprocessor directive. |
8+
| test.c:28:1:28:6 | #endif | Invocation of function memcpy includes a token "#endif" that could be confused for an argument preprocessor directive. |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/PRE32-C/MacroOrFunctionArgsContainHashToken.ql

c/cert/test/rules/PRE32-C/test.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
#include <string.h>
3+
#define MACROFUNCTION(X) strlen(X)
4+
5+
void func(const char *src) {
6+
char *dest;
7+
8+
MACROFUNCTION(
9+
#if NOTDEFINEDMACRO // NON_COMPLIANT
10+
"longstringtest!test!"
11+
#else // NON_COMPLIANT
12+
"shortstring"
13+
#endif // NON_COMPLIANT
14+
);
15+
16+
MACROFUNCTION("alright"); // COMPLIANT
17+
memcpy(dest, src, 12); // COMPLIANT
18+
19+
memcpy(dest, src,
20+
#ifdef SOMEMACRO // NON_COMPLIANT
21+
12
22+
#else // NON_COMPLIANT
23+
24
24+
#endif // NON_COMPLIANT
25+
);
26+
27+
#if TEST // COMPLIANT[FALSE_POSITIVE]
28+
#endif // COMPLIANT[FALSE_POSITIVE]
29+
}

cpp/common/src/codingstandards/cpp/Macro.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,9 @@ class StringizingOperator extends TStringizingOperator {
6363

6464
string toString() { result = getMacro().toString() }
6565
}
66+
67+
68+
pragma[noinline]
69+
predicate isMacroInvocationLocation(MacroInvocation mi, File f, int startChar, int endChar) {
70+
mi.getActualLocation().charLoc(f, startChar, endChar)
71+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import cpp
2+
import codingstandards.cpp.Macro
3+
4+
pragma[noinline]
5+
predicate isPreprocDirectiveLocation(PreprocessorDirective pd, File f, int startChar) {
6+
pd.getLocation().charLoc(f, startChar, _)
7+
}
8+
9+
pragma[noinline]
10+
predicate isPreprocDirectiveLine(PreprocessorDirective pd, File f, int startline, int endline) {
11+
pd.getLocation().hasLocationInfo(f.getAbsolutePath(), startline, _, endline, _)
12+
}
13+
14+
PreprocessorDirective isLocatedInAMacroInvocation(MacroInvocation m) {
15+
exists(PreprocessorDirective p |
16+
// There is not sufficient information in the database for nested macro invocations, because
17+
// the location of nested macros and preprocessor directives are all set to the location of the
18+
// outermost macro invocation
19+
not exists(m.getParentInvocation()) and
20+
exists(File f, int startChar, int endChar |
21+
isMacroInvocationLocation(m, f, startChar, endChar) and
22+
exists(int lStart | isPreprocDirectiveLocation(p, f, lStart) |
23+
// If the start location of the preprocessor directive is after the start of the macro
24+
// invocation, and before the end, it must be within the macro invocation
25+
// Note: it's critical to use startChar < lStart, not startChar <= lStart, because the
26+
// latter will include preprocessor directives which occur in nested macro invocations
27+
startChar < lStart and lStart < endChar
28+
)
29+
) and
30+
result = p
31+
)
32+
}

cpp/common/src/codingstandards/cpp/exclusions/c/Preprocessor5.qll

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@ import cpp
33
import RuleMetadata
44
import codingstandards.cpp.exclusions.RuleMetadata
55

6-
newtype Preprocessor5Query = TMacroParameterNotEnclosedInParenthesesCQueryQuery()
6+
newtype Preprocessor5Query =
7+
TMacroOrFunctionArgsContainHashTokenQuery() or
8+
TMacroParameterNotEnclosedInParenthesesCQueryQuery()
79

810
predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleId) {
11+
query =
12+
// `Query` instance for the `macroOrFunctionArgsContainHashToken` query
13+
Preprocessor5Package::macroOrFunctionArgsContainHashTokenQuery() and
14+
queryId =
15+
// `@id` for the `macroOrFunctionArgsContainHashToken` query
16+
"c/cert/macro-or-function-args-contain-hash-token" and
17+
ruleId = "PRE32-C"
18+
or
919
query =
1020
// `Query` instance for the `macroParameterNotEnclosedInParenthesesCQuery` query
1121
Preprocessor5Package::macroParameterNotEnclosedInParenthesesCQueryQuery() and
@@ -16,6 +26,13 @@ predicate isPreprocessor5QueryMetadata(Query query, string queryId, string ruleI
1626
}
1727

1828
module Preprocessor5Package {
29+
Query macroOrFunctionArgsContainHashTokenQuery() {
30+
//autogenerate `Query` type
31+
result =
32+
// `Query` type for `macroOrFunctionArgsContainHashToken` query
33+
TQueryC(TPreprocessor5PackageQuery(TMacroOrFunctionArgsContainHashTokenQuery()))
34+
}
35+
1936
Query macroParameterNotEnclosedInParenthesesCQueryQuery() {
2037
//autogenerate `Query` type
2138
result =

cpp/common/src/codingstandards/cpp/rules/preprocessingdirectivewithinmacroargument/PreprocessingDirectiveWithinMacroArgument.qll

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,16 @@
55
import cpp
66
import codingstandards.cpp.Customizations
77
import codingstandards.cpp.Exclusions
8+
import codingstandards.cpp.PreprocessorDirective
89

910
abstract class PreprocessingDirectiveWithinMacroArgumentSharedQuery extends Query { }
1011

1112
Query getQuery() { result instanceof PreprocessingDirectiveWithinMacroArgumentSharedQuery }
1213

13-
pragma[noinline]
14-
predicate isMacroInvocationLocation(MacroInvocation mi, File f, int startChar, int endChar) {
15-
mi.getActualLocation().charLoc(f, startChar, endChar)
16-
}
17-
18-
pragma[noinline]
19-
predicate isPreprocDirectiveLocation(PreprocessorDirective pd, File f, int startChar) {
20-
pd.getLocation().charLoc(f, startChar, _)
21-
}
22-
2314
query predicate problems(MacroInvocation m, string message) {
2415
not isExcluded(m, getQuery()) and
2516
exists(PreprocessorDirective p |
26-
// There is not sufficient information in the database for nested macro invocations, because
27-
// the location of nested macros and preprocessor directives are all set to the location of the
28-
// outermost macro invocation
29-
not exists(m.getParentInvocation()) and
30-
exists(File f, int startChar, int endChar |
31-
isMacroInvocationLocation(m, f, startChar, endChar) and
32-
exists(int lStart | isPreprocDirectiveLocation(p, f, lStart) |
33-
// If the start location of the preprocessor directive is after the start of the macro
34-
// invocation, and before the end, it must be within the macro invocation
35-
// Note: it's critical to use startChar < lStart, not startChar <= lStart, because the
36-
// latter will include preprocessor directives which occur in nested macro invocations
37-
startChar < lStart and lStart < endChar
38-
)
39-
) and
17+
p = isLocatedInAMacroInvocation(m) and
4018
message =
4119
"Invocation of macro " + m.getMacroName() + " includes a token \"" + p +
4220
"\" that could be confused for an argument preprocessor directive."

rule_packages/c/Preprocessor5.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
11
{
2+
"CERT-C": {
3+
"PRE32-C": {
4+
"properties": {
5+
"obligation": "rule"
6+
},
7+
"queries": [
8+
{
9+
"description": "Arguments to a function-like macros shall not contain tokens that look like pre-processing directives or else behaviour after macro expansion is unpredictable. This rule applies to functions as well in case they are implemented using macros.",
10+
"kind": "problem",
11+
"name": "Do not use preprocessor directives in invocations of function-like macros",
12+
"precision": "high",
13+
"severity": "warning",
14+
"short_name": "MacroOrFunctionArgsContainHashToken",
15+
"tags": [
16+
"readability",
17+
"correctness"
18+
]
19+
}
20+
],
21+
"title": "Do not use preprocessor directives in invocations of function-like macros"
22+
}
23+
},
224
"MISRA-C-2012": {
325
"RULE-20-7": {
426
"properties": {

rules.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,7 +587,7 @@ c,CERT-C,POS53-C,OutOfScope,Rule,,,Do not use more than one mutex for concurrent
587587
c,CERT-C,POS54-C,OutOfScope,Rule,,,Detect and handle POSIX library errors,,,,
588588
c,CERT-C,PRE30-C,No,Rule,,,Do not create a universal character name through concatenation,,,Medium,
589589
c,CERT-C,PRE31-C,Yes,Rule,,,Avoid side effects in arguments to unsafe macros,RULE-13-2,SideEffects,Medium,
590-
c,CERT-C,PRE32-C,Yes,Rule,,,Do not use preprocessor directives in invocations of function-like macros,,Preprocessor,Hard,
590+
c,CERT-C,PRE32-C,Yes,Rule,,,Do not use preprocessor directives in invocations of function-like macros,,Preprocessor5,Hard,
591591
c,CERT-C,SIG30-C,Yes,Rule,,,Call only asynchronous-safe functions within signal handlers,,Contracts,Medium,
592592
c,CERT-C,SIG31-C,Yes,Rule,,,Do not access shared objects in signal handlers,,Contracts,Medium,
593593
c,CERT-C,SIG34-C,Yes,Rule,,,Do not call signal() from within interruptible signal handlers,,Contracts,Medium,

0 commit comments

Comments
 (0)