C++: Exclusion rules for system macros

Unwanted results were reported for our JPL Rule 24 queries. Including
system headers with complex macros could lead to unpredictable alerts
from these rules.
This commit is contained in:
Jonas Jensen
2021-09-21 11:31:13 +02:00
parent b73a2f7d56
commit 237a7d34b8
3 changed files with 50 additions and 6 deletions

View File

@@ -81,8 +81,8 @@ predicate functionContainsPreprocCode(Function f) {
}
/**
* Holds if `e` is completely or partially from a macro definition, as opposed
* to being passed in as an argument.
* Holds if `e` is completely or partially from a macro invocation `mi`, as
* opposed to being passed in as an argument.
*
* In the following example, the call to `f` is from a macro definition,
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
@@ -93,8 +93,8 @@ predicate functionContainsPreprocCode(Function f) {
* M(y + 1);
* ```
*/
predicate isFromMacroDefinition(Element e) {
exists(MacroInvocation mi, Location eLocation, Location miLocation |
private predicate isFromMacroInvocation(Element e, MacroInvocation mi) {
exists(Location eLocation, Location miLocation |
mi.getAnExpandedElement() = e and
eLocation = e.getLocation() and
miLocation = mi.getLocation() and
@@ -109,3 +109,39 @@ predicate isFromMacroDefinition(Element e) {
eLocation.getEndColumn() >= miLocation.getEndColumn()
)
}
/**
* Holds if `e` is completely or partially from a macro definition, as opposed
* to being passed in as an argument.
*
* In the following example, the call to `f` is from a macro definition,
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
* from `M` refers to a macro.
* ```
* #define M(x) f(x)
* ...
* M(y + 1);
* ```
*/
predicate isFromMacroDefinition(Element e) {
isFromMacroInvocation(e, _)
}
/**
* Holds if `e` is completely or partially from a _system macro_ definition, as
* opposed to being passed in as an argument. A system macro is a macro whose
* definition is outside the source directory of the database.
*
* If the system macro is invoked through a non-system macro, then this
* predicate does not hold. That's a limitation of how macros are represented
* in the database.
*
* See also `isFromMacroDefinition`.
*/
predicate isFromSystemMacroDefinition(Element e) {
exists(MacroInvocation mi |
isFromMacroInvocation(e, mi) and
// Has no relative path in the database, meaning it's a system file.
not exists(mi.getMacro().getFile().getRelativePath())
)
}

View File

@@ -10,6 +10,7 @@
*/
import cpp
import semmle.code.cpp.commons.Exclusions
class OneLineStmt extends Stmt {
OneLineStmt() {
@@ -34,5 +35,8 @@ where
other.onLine(f, line) and toMin = other.getLocation().getStartColumn()
|
toMin
)
) and
// Exclude statements that are from invocations of system-header macros.
// Example: FD_ISSET from glibc.
not isFromSystemMacroDefinition(o)
select o, "This line contains " + cnt + " statements; only one is allowed."

View File

@@ -10,11 +10,15 @@
*/
import cpp
import semmle.code.cpp.commons.Exclusions
from DeclStmt d
where
exists(Variable v1, Variable v2 | v1 = d.getADeclaration() and v2 = d.getADeclaration() |
v1 != v2 and
v1.getLocation().getStartLine() = v2.getLocation().getStartLine()
)
) and
// Exclude declarations that are from invocations of system-header macros.
// Example: FD_ZERO from glibc.
not isFromSystemMacroDefinition(d)
select d, "Multiple variable declarations on the same line."