Skip to content

Commit 7693a3c

Browse files
authored
Merge pull request github#532 from rvermeulen/rvermeulen/fix-424
Fix FP reported in 424
2 parents d087031 + 0f0170a commit 7693a3c

File tree

8 files changed

+142
-66
lines changed

8 files changed

+142
-66
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-`A15-4-4` - `MissingNoExcept.ql`:
2+
- Fix FP reported in #424. Exclude functions calling `std::string::reserve` or `std::string::append` that may throw even if their signatures don't specify it.

cpp/autosar/test/rules/A15-4-4/test.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,17 @@ void test_swap_wrapper() noexcept {
4343
int a = 0;
4444
int b = 1;
4545
swap_wrapper(&a, &b);
46+
}
47+
48+
#include <stdexcept>
49+
#include <string>
50+
51+
std::string test_fp_reported_in_424(
52+
const std::string &s1,
53+
const std::string &s2) { // COMPLIANT - `reserve` and `append` may throw.
54+
std::string s3;
55+
s3.reserve(s1.size() + s2.size());
56+
s3.append(s1.c_str(), s1.size());
57+
s3.append(s2.c_str(), s2.size());
58+
return s3;
4659
}

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

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import cpp
66
import codingstandards.cpp.standardlibrary.Exceptions
77
import codingstandards.cpp.exceptions.ExceptionSpecifications
8+
import codingstandards.cpp.exceptions.ExceptionFlowCustomizations
89
import ThirdPartyExceptions
910

1011
/*
@@ -271,72 +272,6 @@ ExceptionType getAFunctionThrownType(Function f, ThrowingExpr throwingExpr) {
271272
)
272273
}
273274

274-
/** A `ThrowingExpr` which is the origin of a exceptions in the program. */
275-
abstract class OriginThrowingExpr extends ThrowingExpr { }
276-
277-
/** An expression which directly throws. */
278-
class DirectThrowExprThrowingExpr extends DirectThrowExpr, OriginThrowingExpr {
279-
override ExceptionType getAnExceptionType() { result = getExceptionType() }
280-
}
281-
282-
/** An `typeid` expression which may throw `std::bad_typeid`. */
283-
class TypeIdThrowingExpr extends TypeidOperator, OriginThrowingExpr {
284-
override ExceptionType getAnExceptionType() { result instanceof StdBadTypeId }
285-
}
286-
287-
/** An `new[]` expression which may throw `std::bad_array_new_length`. */
288-
class NewThrowingExpr extends NewArrayExpr, OriginThrowingExpr {
289-
NewThrowingExpr() {
290-
// If the extent is known to be below 0 at runtime
291-
getExtent().getValue().toInt() < 0
292-
or
293-
// initializer has more elements than the array size
294-
getExtent().getValue().toInt() < getInitializer().(ArrayAggregateLiteral).getArraySize()
295-
}
296-
297-
override ExceptionType getAnExceptionType() { result instanceof StdBadArrayNewLength }
298-
}
299-
300-
/** A `ReThrowExpr` which throws a previously caught exception. */
301-
class ReThrowExprThrowingExpr extends ReThrowExpr, ThrowingExpr {
302-
predicate rethrows(CatchBlock cb, ExceptionType et, ThrowingExpr te) {
303-
// Find the nearest CatchBlock
304-
cb = getNearestCatch(this.getEnclosingStmt()) and
305-
// Find an `ExceptionType` which is caught by this catch block, and `ThrowingExpr` which throws that exception type
306-
catches(cb, te, et)
307-
}
308-
309-
override ExceptionType getAnExceptionType() { rethrows(_, result, _) }
310-
311-
CatchBlock getCatchBlock() { rethrows(result, _, _) }
312-
}
313-
314-
/** An expression which calls a function which may throw an exception. */
315-
class FunctionCallThrowingExpr extends FunctionCall, ThrowingExpr {
316-
override ExceptionType getAnExceptionType() {
317-
exists(Function target |
318-
target = getTarget() and
319-
result = getAFunctionThrownType(target, _) and
320-
// [expect.spec] states that throwing an exception type that is prohibited
321-
// by the specification will result in the program terminating, unless
322-
// a custom `unexpected_handler` is registered that throws an exception type
323-
// which is compatible with the dynamic exception specification, or the
324-
// dynamic exception specification lists `std::bad_exception`, in which case
325-
// a `std::bad_exception` is thrown.
326-
// As dynamic exception specifications and the `unexpected_handler` are both
327-
// deprecated in C++14 and removed in C++17, we assume a default
328-
// `std::unexpected` handler that calls `std::terminate` and therefore
329-
// do not propagate such exceptions to the call sites for the function.
330-
not (
331-
hasDynamicExceptionSpecification(target) and
332-
not result = getAHandledExceptionType(target.getAThrownType())
333-
or
334-
isNoExceptTrue(target)
335-
)
336-
)
337-
}
338-
}
339-
340275
module ExceptionPathGraph {
341276
/**
342277
* A function for which we want path information.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* A library customize models that model the flow of exceptions through the program.
3+
*/
4+
5+
import cpp
6+
private import codingstandards.cpp.exceptions.ExceptionFlow
7+
8+
/** A `ThrowingExpr` which is the origin of a exceptions in the program. */
9+
abstract class OriginThrowingExpr extends ThrowingExpr { }
10+
11+
/**
12+
* A `FunctionCall` to an external function without an exception specification that *
13+
* may throw an exception.
14+
*/
15+
abstract class ExternalUnderspecifiedFunctionCallThrowingExpr extends FunctionCall, ThrowingExpr { }
16+
17+
/**
18+
* An extensible predicate that describes functions that when called may throw an exception.
19+
*/
20+
extensible predicate throwingFunctionModel(
21+
string functionNamespaceQualifier, string functionTypeQualifier, string functionName,
22+
string exceptionNamespaceQualifier, string exceptionType
23+
);
24+
25+
/**
26+
* A `FunctionCall` that may throw an exception of type `ExceptionType` as provded by
27+
* the extensible predicate `throwingFunctionModel`.
28+
*/
29+
private class ExternalFunctionCallThrowingExpr extends FunctionCall, ThrowingExpr {
30+
ExceptionType exceptionType;
31+
32+
ExternalFunctionCallThrowingExpr() {
33+
exists(
34+
string functionNamespaceQualifier, string functionTypeQualifier, string functionName,
35+
string exceptionNamespaceQualifier, string exceptionTypeSpec
36+
|
37+
throwingFunctionModel(functionNamespaceQualifier, functionTypeQualifier, functionName,
38+
exceptionNamespaceQualifier, exceptionTypeSpec) and
39+
this.getTarget()
40+
.hasQualifiedName(functionNamespaceQualifier, functionTypeQualifier, functionName) and
41+
exceptionType.(Class).hasQualifiedName(exceptionNamespaceQualifier, exceptionTypeSpec)
42+
)
43+
}
44+
45+
override ExceptionType getAnExceptionType() { result = exceptionType }
46+
}
47+
48+
/** An expression which directly throws. */
49+
class DirectThrowExprThrowingExpr extends DirectThrowExpr, OriginThrowingExpr {
50+
override ExceptionType getAnExceptionType() { result = getExceptionType() }
51+
}
52+
53+
/** A `ReThrowExpr` which throws a previously caught exception. */
54+
class ReThrowExprThrowingExpr extends ReThrowExpr, ThrowingExpr {
55+
predicate rethrows(CatchBlock cb, ExceptionType et, ThrowingExpr te) {
56+
// Find the nearest CatchBlock
57+
cb = getNearestCatch(this.getEnclosingStmt()) and
58+
// Find an `ExceptionType` which is caught by this catch block, and `ThrowingExpr` which throws that exception type
59+
catches(cb, te, et)
60+
}
61+
62+
override ExceptionType getAnExceptionType() { rethrows(_, result, _) }
63+
64+
CatchBlock getCatchBlock() { rethrows(result, _, _) }
65+
}
66+
67+
/** An expression which calls a function which may throw an exception. */
68+
class FunctionCallThrowingExpr extends FunctionCall, ThrowingExpr {
69+
override ExceptionType getAnExceptionType() {
70+
exists(Function target |
71+
target = getTarget() and
72+
result = getAFunctionThrownType(target, _) and
73+
// [expect.spec] states that throwing an exception type that is prohibited
74+
// by the specification will result in the program terminating, unless
75+
// a custom `unexpected_handler` is registered that throws an exception type
76+
// which is compatible with the dynamic exception specification, or the
77+
// dynamic exception specification lists `std::bad_exception`, in which case
78+
// a `std::bad_exception` is thrown.
79+
// As dynamic exception specifications and the `unexpected_handler` are both
80+
// deprecated in C++14 and removed in C++17, we assume a default
81+
// `std::unexpected` handler that calls `std::terminate` and therefore
82+
// do not propagate such exceptions to the call sites for the function.
83+
not (
84+
hasDynamicExceptionSpecification(target) and
85+
not result = getAHandledExceptionType(target.getAThrownType())
86+
or
87+
isNoExceptTrue(target)
88+
)
89+
)
90+
or
91+
result = this.(ExternalUnderspecifiedFunctionCallThrowingExpr).getAnExceptionType()
92+
or
93+
result = this.(ExternalFunctionCallThrowingExpr).getAnExceptionType()
94+
}
95+
}
96+
97+
/** An `typeid` expression which may throw `std::bad_typeid`. */
98+
private class TypeIdThrowingExpr extends TypeidOperator, OriginThrowingExpr {
99+
override ExceptionType getAnExceptionType() { result instanceof StdBadTypeId }
100+
}
101+
102+
/** An `new[]` expression which may throw `std::bad_array_new_length`. */
103+
private class NewThrowingExpr extends NewArrayExpr, OriginThrowingExpr {
104+
NewThrowingExpr() {
105+
// If the extent is known to be below 0 at runtime
106+
getExtent().getValue().toInt() < 0
107+
or
108+
// initializer has more elements than the array size
109+
getExtent().getValue().toInt() < getInitializer().(ArrayAggregateLiteral).getArraySize()
110+
}
111+
112+
override ExceptionType getAnExceptionType() { result instanceof StdBadArrayNewLength }
113+
}

cpp/common/src/ext/stdc++.model.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/common-cpp-coding-standards
4+
extensible: throwingFunctionModel
5+
data:
6+
- ["std", "basic_string", "append", "std", "out_of_range"]
7+
- ["std", "basic_string", "reserve", "std", "length_error"]

cpp/common/src/qlpack.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ version: 2.22.0-dev
33
license: MIT
44
dependencies:
55
codeql/cpp-all: 0.9.3
6+
dataExtensions:
7+
- ext/*.model.yml

cpp/common/test/includes/standard-library/stdexcept.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,7 @@ class nested_exception {
2828
template <typename T> [[noreturn]] void throw_with_nested(T &&t);
2929
template <typename E> void rethrow_if_nested(E const &e);
3030

31+
class length_error : public logic_error{};
32+
class out_of_range: public logic_error{};
3133
} // namespace std
3234
#endif // _GHLIBCPP_STDEXCEPT

cpp/common/test/includes/standard-library/string

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ public:
166166
int compare(const charT *s) const;
167167
int compare(size_type pos1, size_type n1, const charT *s) const;
168168
int compare(size_type pos1, size_type n1, const charT *s, size_type n2) const;
169+
170+
void reserve(size_type new_cap = 0);
169171
};
170172

171173
template <class charT, class traits, class Allocator>

0 commit comments

Comments
 (0)