Skip to content

Commit da1d12e

Browse files
committed
A15-2-2: Avoid infinite interpretation edge case
In CodeQL CLI 2.12.7 there is a bug which causes an infinite loop during results interpretation when a result includes more than maxPaths paths and also includes a path with no edges i.e. where the source and sink node are the same. To avoid this edge case, if we report a path where the source and sink are the same (i.e the throwingExpr directly throws an exception), we adjust the sink node to report the constructor, which creates a one step path from the throwingExprFlowNode to the constructor node. This also means we can delete the `nodes` query predicate, as we only included it to enable zero-path elements to display.
1 parent afc027b commit da1d12e

File tree

3 files changed

+42
-48
lines changed

3 files changed

+42
-48
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- `A15-2-2` - all results now include an associated exception flow path to avoid a CodeQL CLI bug in 2.12.7. This includes results where an exception is thrown directly in the constructor.

cpp/autosar/src/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.ql

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class DeleteWrapperFunction extends Function {
7878
Parameter getADeleteParameter() { result = p }
7979
}
8080

81+
class ExceptionThrowingConstructor extends ExceptionThrowingFunction, Constructor {
82+
ExceptionThrowingConstructor() {
83+
exists(getAFunctionThrownType(this, _)) and
84+
// The constructor is within the users source code
85+
exists(getFile().getRelativePath())
86+
}
87+
}
88+
8189
class ExceptionThrownInConstructor extends ExceptionThrowingExpr {
8290
Constructor c;
8391

@@ -90,24 +98,20 @@ class ExceptionThrownInConstructor extends ExceptionThrowingExpr {
9098
Constructor getConstructor() { result = c }
9199
}
92100

93-
/**
94-
* Add the `nodes` predicate to ensure results with an empty path are still reported.
95-
*/
96-
query predicate nodes(ExceptionFlowNode node) { any() }
97-
98101
from
99-
Constructor c, ExceptionThrownInConstructor throwingExpr, NewAllocationExpr newExpr,
100-
ExceptionFlowNode exceptionSource, ExceptionFlowNode functionNode
102+
ExceptionThrowingConstructor c, ExceptionThrownInConstructor throwingExpr,
103+
NewAllocationExpr newExpr, ExceptionFlowNode exceptionSource,
104+
ExceptionFlowNode throwingExprFlowNode, ExceptionFlowNode reportingNode
101105
where
102106
not isExcluded(c, Exceptions2Package::constructorErrorLeavesObjectInInvalidStateQuery()) and
103107
not isNoExceptTrue(c) and
104108
// Constructor must exit with an exception
105109
c = throwingExpr.getConstructor() and
106-
throwingExpr.hasExceptionFlowReflexive(exceptionSource, functionNode, _) and
110+
throwingExpr.hasExceptionFlowReflexive(exceptionSource, throwingExprFlowNode, _) and
107111
exists(ExceptionFlowNode mid |
108112
edges*(exceptionSource, mid) and
109113
newExpr.getASuccessor+() = mid.asThrowingExpr() and
110-
edges*(mid, functionNode) and
114+
edges*(mid, throwingExprFlowNode) and
111115
not exists(ExceptionFlowNode prior | edges(prior, mid) |
112116
prior.asCatchBlock().getEnclosingFunction() = c
113117
)
@@ -126,7 +130,16 @@ where
126130
DataFlow::localFlow(DataFlow::exprNode(newExpr), DataFlow::exprNode(deletedExpr)) and
127131
newExpr.getASuccessor+() = deletedExpr and
128132
deletedExpr.getASuccessor+() = throwingExpr
129-
)
130-
select c, exceptionSource, functionNode, "Constructor throws $@ and allocates memory at $@",
133+
) and
134+
// In CodeQL CLI 2.12.7 there is a bug which causes an infinite loop during results interpretation
135+
// when a result includes more than maxPaths paths and also includes a path with no edges i.e.
136+
// where the source and sink node are the same.
137+
// To avoid this edge case, if we report a path where the source and sink are the same (i.e the
138+
// throwingExpr directly throws an exception), we adjust the sink node to report the constructor,
139+
// which creates a one step path from the throwingExprFlowNode to the constructor node.
140+
if throwingExprFlowNode = exceptionSource
141+
then reportingNode.asFunction() = c and edges(throwingExprFlowNode, reportingNode)
142+
else reportingNode = throwingExprFlowNode
143+
select c, exceptionSource, reportingNode, "Constructor throws $@ and allocates memory at $@",
131144
throwingExpr, throwingExpr.(ThrowingExpr).getAnExceptionType().getExceptionName(), newExpr,
132145
"alloc"

cpp/autosar/test/rules/A15-2-2/ConstructorErrorLeavesObjectInInvalidState.expected

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,40 @@ edges
33
| test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:14:33:16:5 | { ... } [exception] |
44
| test.cpp:14:33:16:5 | { ... } [bad_alloc] | test.cpp:15:7:15:11 | re-throw exception [bad_alloc] |
55
| test.cpp:14:33:16:5 | { ... } [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] |
6+
| test.cpp:15:7:15:11 | re-throw exception [bad_alloc] | test.cpp:9:3:9:8 | ClassA [bad_alloc] |
7+
| test.cpp:15:7:15:11 | re-throw exception [exception] | test.cpp:9:3:9:8 | ClassA [exception] |
68
| test.cpp:25:16:25:27 | new [bad_alloc] | test.cpp:27:33:30:5 | { ... } [bad_alloc] |
79
| test.cpp:26:7:26:28 | throw ... [exception] | test.cpp:27:33:30:5 | { ... } [exception] |
810
| test.cpp:27:33:30:5 | { ... } [bad_alloc] | test.cpp:29:7:29:11 | re-throw exception [bad_alloc] |
911
| test.cpp:27:33:30:5 | { ... } [exception] | test.cpp:29:7:29:11 | re-throw exception [exception] |
12+
| test.cpp:29:7:29:11 | re-throw exception [bad_alloc] | test.cpp:23:3:23:8 | ClassB [bad_alloc] |
13+
| test.cpp:29:7:29:11 | re-throw exception [exception] | test.cpp:23:3:23:8 | ClassB [exception] |
1014
| test.cpp:44:16:44:27 | call to CreateMember [bad_alloc] | test.cpp:46:33:48:5 | { ... } [bad_alloc] |
1115
| test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:46:33:48:5 | { ... } [exception] |
1216
| test.cpp:46:33:48:5 | { ... } [bad_alloc] | test.cpp:47:7:47:11 | re-throw exception [bad_alloc] |
1317
| test.cpp:46:33:48:5 | { ... } [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] |
18+
| test.cpp:47:7:47:11 | re-throw exception [bad_alloc] | test.cpp:41:3:41:8 | ClassC [bad_alloc] |
19+
| test.cpp:47:7:47:11 | re-throw exception [exception] | test.cpp:41:3:41:8 | ClassC [exception] |
1420
| test.cpp:58:16:58:27 | call to CreateMember [bad_alloc] | test.cpp:60:33:63:5 | { ... } [bad_alloc] |
1521
| test.cpp:59:7:59:28 | throw ... [exception] | test.cpp:60:33:63:5 | { ... } [exception] |
1622
| test.cpp:60:33:63:5 | { ... } [bad_alloc] | test.cpp:62:7:62:11 | re-throw exception [bad_alloc] |
1723
| test.cpp:60:33:63:5 | { ... } [exception] | test.cpp:62:7:62:11 | re-throw exception [exception] |
24+
| test.cpp:62:7:62:11 | re-throw exception [bad_alloc] | test.cpp:55:3:55:8 | ClassD [bad_alloc] |
25+
| test.cpp:62:7:62:11 | re-throw exception [exception] | test.cpp:55:3:55:8 | ClassD [exception] |
26+
| test.cpp:77:11:77:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] |
27+
| test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] |
1828
| test.cpp:80:13:80:22 | new [bad_alloc] | test.cpp:82:33:86:5 | { ... } [bad_alloc] |
1929
| test.cpp:81:13:81:22 | new [bad_alloc] | test.cpp:82:33:86:5 | { ... } [bad_alloc] |
2030
| test.cpp:82:33:86:5 | { ... } [bad_alloc] | test.cpp:85:7:85:11 | re-throw exception [bad_alloc] |
21-
nodes
22-
| test.cpp:12:16:12:27 | new [bad_alloc] |
23-
| test.cpp:13:7:13:28 | throw ... [exception] |
24-
| test.cpp:14:33:16:5 | { ... } [bad_alloc] |
25-
| test.cpp:14:33:16:5 | { ... } [exception] |
26-
| test.cpp:15:7:15:11 | re-throw exception [bad_alloc] |
27-
| test.cpp:15:7:15:11 | re-throw exception [exception] |
28-
| test.cpp:25:16:25:27 | new [bad_alloc] |
29-
| test.cpp:26:7:26:28 | throw ... [exception] |
30-
| test.cpp:27:33:30:5 | { ... } [bad_alloc] |
31-
| test.cpp:27:33:30:5 | { ... } [exception] |
32-
| test.cpp:29:7:29:11 | re-throw exception [bad_alloc] |
33-
| test.cpp:29:7:29:11 | re-throw exception [exception] |
34-
| test.cpp:44:16:44:27 | call to CreateMember [bad_alloc] |
35-
| test.cpp:45:7:45:28 | throw ... [exception] |
36-
| test.cpp:46:33:48:5 | { ... } [bad_alloc] |
37-
| test.cpp:46:33:48:5 | { ... } [exception] |
38-
| test.cpp:47:7:47:11 | re-throw exception [bad_alloc] |
39-
| test.cpp:47:7:47:11 | re-throw exception [exception] |
40-
| test.cpp:58:16:58:27 | call to CreateMember [bad_alloc] |
41-
| test.cpp:59:7:59:28 | throw ... [exception] |
42-
| test.cpp:60:33:63:5 | { ... } [bad_alloc] |
43-
| test.cpp:60:33:63:5 | { ... } [exception] |
44-
| test.cpp:62:7:62:11 | re-throw exception [bad_alloc] |
45-
| test.cpp:62:7:62:11 | re-throw exception [exception] |
46-
| test.cpp:77:11:77:20 | new [bad_alloc] |
47-
| test.cpp:78:11:78:20 | new [bad_alloc] |
48-
| test.cpp:80:13:80:22 | new [bad_alloc] |
49-
| test.cpp:81:13:81:22 | new [bad_alloc] |
50-
| test.cpp:82:33:86:5 | { ... } [bad_alloc] |
51-
| test.cpp:85:7:85:11 | re-throw exception [bad_alloc] |
52-
| test.cpp:87:11:87:20 | new [bad_alloc] |
31+
| test.cpp:85:7:85:11 | re-throw exception [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] |
32+
| test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] |
5333
#select
5434
| test.cpp:9:3:9:8 | ClassA | test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:15:7:15:11 | re-throw exception | std::bad_alloc | test.cpp:12:16:12:27 | new | alloc |
5535
| test.cpp:9:3:9:8 | ClassA | test.cpp:13:7:13:28 | throw ... [exception] | test.cpp:15:7:15:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:15:7:15:11 | re-throw exception | std::exception | test.cpp:12:16:12:27 | new | alloc |
5636
| test.cpp:41:3:41:8 | ClassC | test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:47:7:47:11 | re-throw exception | std::bad_alloc | test.cpp:44:16:44:27 | call to CreateMember | alloc |
5737
| test.cpp:41:3:41:8 | ClassC | test.cpp:45:7:45:28 | throw ... [exception] | test.cpp:47:7:47:11 | re-throw exception [exception] | Constructor throws $@ and allocates memory at $@ | test.cpp:47:7:47:11 | re-throw exception | std::exception | test.cpp:44:16:44:27 | call to CreateMember | alloc |
58-
| test.cpp:75:3:75:8 | ClassE | test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:78:11:78:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:78:11:78:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc |
59-
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc |
60-
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:78:11:78:20 | new | alloc |
61-
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:80:13:80:22 | new | alloc |
62-
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:87:11:87:20 | new [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:81:13:81:22 | new | alloc |
38+
| test.cpp:75:3:75:8 | ClassE | test.cpp:78:11:78:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:78:11:78:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc |
39+
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:77:11:77:20 | new | alloc |
40+
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:78:11:78:20 | new | alloc |
41+
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:80:13:80:22 | new | alloc |
42+
| test.cpp:75:3:75:8 | ClassE | test.cpp:87:11:87:20 | new [bad_alloc] | test.cpp:75:3:75:8 | ClassE [bad_alloc] | Constructor throws $@ and allocates memory at $@ | test.cpp:87:11:87:20 | new | std::bad_alloc | test.cpp:81:13:81:22 | new | alloc |

0 commit comments

Comments
 (0)