Skip to content

Commit fc00564

Browse files
Implement package exceptions3 for MISRA C++
Adds support for: - RULE-18-3-1 - RULE-18-3-2 - RULE-18-4-1
1 parent 01b11af commit fc00564

33 files changed

+802
-5
lines changed

cpp/common/src/codingstandards/cpp/Function.qll

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,74 @@ import cpp
88
class PopCount extends Function {
99
PopCount() { this.getName().toLowerCase().matches("%popc%nt%") }
1010
}
11+
12+
/**
13+
* A function the user explicitly declared, though its body may be compiler-generated.
14+
*/
15+
class UserDeclaredFunction extends Function {
16+
UserDeclaredFunction() {
17+
not isDeleted() and
18+
(
19+
// Not compiler generated is explicitly declared
20+
not isCompilerGenerated()
21+
or
22+
// Closure functions are compiler generated, but the user
23+
// explicitly declared the lambda expression.
24+
exists(Closure c | c.getLambdaFunction() = this)
25+
or
26+
// Indicates users specifically wrote "= default"
27+
isDefaulted()
28+
)
29+
}
30+
}
31+
32+
/**
33+
* An expression that is effectively a function access.
34+
*
35+
* There are more ways to effectively access a function than a basic `FunctionAcess`, for instance
36+
* taking the address of a function access, and declaring/converting a lambda function.
37+
*/
38+
abstract class FunctionAccessLikeExpr extends Expr {
39+
abstract Function getFunction();
40+
}
41+
42+
/**
43+
* A basic function access expression.
44+
*/
45+
class FunctionAccessExpr extends FunctionAccess, FunctionAccessLikeExpr {
46+
override Function getFunction() { result = this.getTarget() }
47+
}
48+
49+
/**
50+
* Taking an address of a function access in effectively a function access.
51+
*/
52+
class FunctionAddressExpr extends AddressOfExpr, FunctionAccessLikeExpr {
53+
Function func;
54+
55+
FunctionAddressExpr() { func = getOperand().(FunctionAccessLikeExpr).getFunction() }
56+
57+
override Function getFunction() { result = func }
58+
}
59+
60+
/**
61+
* An expression that declares a lambda function is essentially a function access of the lambda
62+
* function body.
63+
*/
64+
class LambdaValueExpr extends LambdaExpression, FunctionAccessLikeExpr {
65+
override Function getFunction() { result = this.(LambdaExpression).getLambdaFunction() }
66+
}
67+
68+
/**
69+
* When a lambda is converted via conversion operator to a function pointer, it is
70+
* effectively a function access of the lambda function.
71+
*/
72+
class LambdaConversionExpr extends FunctionCall, FunctionAccessLikeExpr {
73+
Function func;
74+
75+
LambdaConversionExpr() {
76+
getTarget() instanceof ConversionOperator and
77+
func = getQualifier().(LambdaValueExpr).getFunction()
78+
}
79+
80+
override Function getFunction() { result = func }
81+
}

cpp/common/src/codingstandards/cpp/exceptions/ExceptionFlow.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ TryStmt getNearestTry(Stmt s) {
121121
else result = getNearestTry(s.getParent())
122122
}
123123

124+
/** Gets a try statement that contains the given statement. */
125+
TryStmt getATryStmt(Stmt s) {
126+
result.getStmt() = s.getEnclosingStmt().getEnclosingBlock*()
127+
}
128+
124129
/** Gets the nearest parent catch block for the given statement. */
125130
CatchBlock getNearestCatch(Stmt s) {
126131
if s.getParent() instanceof CatchBlock

cpp/common/src/codingstandards/cpp/exceptions/SpecialFunctionExceptions.qll

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,102 @@
1616

1717
import cpp
1818
private import ExceptionFlow
19+
private import codingstandards.cpp.Function
1920
private import codingstandards.cpp.Swap
2021
private import codingstandards.cpp.EncapsulatingFunctions
2122
private import codingstandards.cpp.exceptions.ExceptionFlow
2223

2324
/** A special function. */
2425
class SpecialFunction extends Function {
26+
string specialDescription;
27+
2528
SpecialFunction() {
26-
this instanceof MoveAssignmentOperator
29+
this instanceof MoveAssignmentOperator and
30+
specialDescription = "move assignment operator"
2731
or
28-
this instanceof MoveConstructor
32+
this instanceof MoveConstructor and
33+
specialDescription = "move constructor"
2934
or
30-
this instanceof Destructor
35+
this instanceof Destructor and
36+
specialDescription = "destructor"
3137
or
32-
this instanceof DeallocationFunction
38+
this instanceof DeallocationFunction and
39+
specialDescription = "deallocation function"
3340
or
34-
this instanceof SwapFunction
41+
this instanceof SwapFunction and
42+
specialDescription = "swap function"
43+
}
44+
45+
string getSpecialDescription() { result = specialDescription }
46+
}
47+
48+
/**
49+
* A function which isn't special by itself, but is used in a special context.
50+
*
51+
* For example, functions which are reachable from initializers of static or thread-local
52+
* variables can result in implementation-defined behavior if they throw exceptions.
53+
*/
54+
abstract class SpecialUseOfFunction extends Expr {
55+
abstract Function getFunction();
56+
57+
abstract string getSpecialDescription(Locatable extra, string extraString);
58+
}
59+
60+
class FunctionUsedInInitializer extends FunctionCall, SpecialUseOfFunction {
61+
Variable var;
62+
63+
FunctionUsedInInitializer() {
64+
(var.isStatic() or var.isThreadLocal() or var.isTopLevel()) and
65+
exists(Expr initializer |
66+
var.getInitializer().getExpr() = initializer and
67+
getParent*() = initializer
68+
)
69+
}
70+
71+
override Function getFunction() { result = this.getTarget() }
72+
73+
override string getSpecialDescription(Locatable extra, string extraString) {
74+
result = "used to initialize variable $@" and
75+
extra = var and
76+
extraString = var.getName()
77+
}
78+
}
79+
80+
class FunctionGivenToStdExitHandler extends FunctionAccess, SpecialUseOfFunction {
81+
Function exitHandler;
82+
FunctionCall exitHandlerCall;
83+
84+
FunctionGivenToStdExitHandler() {
85+
exitHandler.hasGlobalOrStdName(["atexit", "at_quick_exit", "set_terminate"]) and
86+
exitHandlerCall.getTarget() = exitHandler and
87+
exitHandlerCall.getArgument(0) = this
88+
}
89+
90+
override Function getFunction() { result = getTarget() }
91+
92+
override string getSpecialDescription(Locatable extra, string extraString) {
93+
result = "$@" and
94+
extra = exitHandlerCall and
95+
extraString = "passed exit handler std::" + exitHandler.getName()
96+
}
97+
}
98+
99+
class FunctionPassedToExternC extends FunctionAccessLikeExpr, SpecialUseOfFunction {
100+
Function cFunction;
101+
FunctionCall cFunctionCall;
102+
103+
FunctionPassedToExternC() {
104+
cFunction.hasCLinkage() and
105+
cFunction = cFunctionCall.getTarget() and
106+
cFunctionCall.getAnArgument() = this
107+
}
108+
109+
override Function getFunction() { result = this.(FunctionAccessLikeExpr).getFunction() }
110+
111+
override string getSpecialDescription(Locatable extra, string extraString) {
112+
result = "$@ extern \"C\" function '" + cFunction.getName() + "'" and
113+
extra = cFunctionCall and
114+
extraString = "passed to"
35115
}
36116
}
37117

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Exceptions3Query =
7+
TMissingCatchAllExceptionHandlerInMainQuery() or
8+
TClassExceptionCaughtByValueQuery() or
9+
TExceptionUnfriendlyFunctionMustBeNoexceptQuery()
10+
11+
predicate isExceptions3QueryMetadata(Query query, string queryId, string ruleId, string category) {
12+
query =
13+
// `Query` instance for the `missingCatchAllExceptionHandlerInMain` query
14+
Exceptions3Package::missingCatchAllExceptionHandlerInMainQuery() and
15+
queryId =
16+
// `@id` for the `missingCatchAllExceptionHandlerInMain` query
17+
"cpp/misra/missing-catch-all-exception-handler-in-main" and
18+
ruleId = "RULE-18-3-1" and
19+
category = "advisory"
20+
or
21+
query =
22+
// `Query` instance for the `classExceptionCaughtByValue` query
23+
Exceptions3Package::classExceptionCaughtByValueQuery() and
24+
queryId =
25+
// `@id` for the `classExceptionCaughtByValue` query
26+
"cpp/misra/class-exception-caught-by-value" and
27+
ruleId = "RULE-18-3-2" and
28+
category = "required"
29+
or
30+
query =
31+
// `Query` instance for the `exceptionUnfriendlyFunctionMustBeNoexcept` query
32+
Exceptions3Package::exceptionUnfriendlyFunctionMustBeNoexceptQuery() and
33+
queryId =
34+
// `@id` for the `exceptionUnfriendlyFunctionMustBeNoexcept` query
35+
"cpp/misra/exception-unfriendly-function-must-be-noexcept" and
36+
ruleId = "RULE-18-4-1" and
37+
category = "required"
38+
}
39+
40+
module Exceptions3Package {
41+
Query missingCatchAllExceptionHandlerInMainQuery() {
42+
//autogenerate `Query` type
43+
result =
44+
// `Query` type for `missingCatchAllExceptionHandlerInMain` query
45+
TQueryCPP(TExceptions3PackageQuery(TMissingCatchAllExceptionHandlerInMainQuery()))
46+
}
47+
48+
Query classExceptionCaughtByValueQuery() {
49+
//autogenerate `Query` type
50+
result =
51+
// `Query` type for `classExceptionCaughtByValue` query
52+
TQueryCPP(TExceptions3PackageQuery(TClassExceptionCaughtByValueQuery()))
53+
}
54+
55+
Query exceptionUnfriendlyFunctionMustBeNoexceptQuery() {
56+
//autogenerate `Query` type
57+
result =
58+
// `Query` type for `exceptionUnfriendlyFunctionMustBeNoexcept` query
59+
TQueryCPP(TExceptions3PackageQuery(TExceptionUnfriendlyFunctionMustBeNoexceptQuery()))
60+
}
61+
}

cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import Declarations
1717
import ExceptionSafety
1818
import Exceptions1
1919
import Exceptions2
20+
import Exceptions3
2021
import Expressions
2122
import FloatingPoint
2223
import Freed
@@ -72,6 +73,7 @@ newtype TCPPQuery =
7273
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
7374
TExceptions1PackageQuery(Exceptions1Query q) or
7475
TExceptions2PackageQuery(Exceptions2Query q) or
76+
TExceptions3PackageQuery(Exceptions3Query q) or
7577
TExpressionsPackageQuery(ExpressionsQuery q) or
7678
TFloatingPointPackageQuery(FloatingPointQuery q) or
7779
TFreedPackageQuery(FreedQuery q) or
@@ -127,6 +129,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
127129
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
128130
isExceptions1QueryMetadata(query, queryId, ruleId, category) or
129131
isExceptions2QueryMetadata(query, queryId, ruleId, category) or
132+
isExceptions3QueryMetadata(query, queryId, ruleId, category) or
130133
isExpressionsQueryMetadata(query, queryId, ruleId, category) or
131134
isFloatingPointQueryMetadata(query, queryId, ruleId, category) or
132135
isFreedQueryMetadata(query, queryId, ruleId, category) or
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @id cpp/misra/missing-catch-all-exception-handler-in-main
3+
* @name RULE-18-3-1: There should be at least one exception handler to catch all otherwise unhandled exceptions
4+
* @description The main function should have a catch-all exception handler (catch(...)) to catch
5+
* all otherwise unhandled exceptions.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity warning
9+
* @tags external/misra/id/rule-18-3-1
10+
* scope/single-translation-unit
11+
* maintainability
12+
* external/misra/enforcement/decidable
13+
* external/misra/obligation/advisory
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
import codingstandards.cpp.exceptions.ExceptionFlow
19+
import codingstandards.cpp.exceptions.ExceptionSpecifications
20+
21+
/**
22+
* A function call in main that is not declared noexcept and is not within a catch-all
23+
* exception handler (catch(...)).
24+
*/
25+
class UncaughtFunctionCallInMain extends FunctionCall {
26+
UncaughtFunctionCallInMain() {
27+
getEnclosingFunction().hasName("main") and
28+
not isNoExceptTrue(getTarget()) and
29+
not exists(TryStmt try |
30+
try = getATryStmt(this.getEnclosingStmt()) and
31+
try.getCatchClause(_) instanceof CatchAnyBlock
32+
)
33+
}
34+
35+
/**
36+
* We only want to report one counter-example indicating a missing catch(...), so this holds only
37+
* for the first one we find.
38+
*/
39+
predicate isFirst() {
40+
this =
41+
rank[1](UncaughtFunctionCallInMain fc |
42+
any()
43+
|
44+
fc order by fc.getLocation().getStartLine(), fc.getLocation().getStartColumn()
45+
)
46+
}
47+
}
48+
49+
from Function f, UncaughtFunctionCallInMain fc
50+
where
51+
not isExcluded(f, Exceptions3Package::missingCatchAllExceptionHandlerInMainQuery()) and
52+
f.getName() = "main" and
53+
fc.isFirst()
54+
select f,
55+
"Main function has a $@ which is not within a try block with a catch-all ('catch(...)') handler.",
56+
fc, "function call that may throw"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/**
2+
* @id cpp/misra/class-exception-caught-by-value
3+
* @name RULE-18-3-2: An exception of class type shall be caught by const reference or reference
4+
* @description Catching exception classes by value can lead to slicing, which can result in
5+
* unexpected behavior.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-18-3-2
10+
* scope/single-translation-unit
11+
* correctness
12+
* maintainability
13+
* external/misra/enforcement/decidable
14+
* external/misra/obligation/required
15+
*/
16+
17+
import cpp
18+
import codingstandards.cpp.misra
19+
20+
from CatchBlock catch, Type type
21+
where
22+
not isExcluded(catch, Exceptions3Package::classExceptionCaughtByValueQuery()) and
23+
type = catch.getParameter().getType() and
24+
type.stripTopLevelSpecifiers() instanceof Class
25+
select catch, "Catch block catches a class type '" + type + "' by value, which can lead to slicing."

0 commit comments

Comments
 (0)