Skip to content

Commit 6c8ba57

Browse files
authored
Merge pull request #515 from rvermeulen/rvermeulen/fix-issue-#311
Fix #370
2 parents e300f67 + ad49b87 commit 6c8ba57

File tree

7 files changed

+165
-35
lines changed

7 files changed

+165
-35
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- `A8-4-8` - `OutParametersUsed.ql`
2+
- Fixes #370 - Non-member user-defined assignment operator and stream insertion/extraction parameters that are required to be out parameters are excluded.
3+
- Broadens the definition of out parameter by considering assignment and crement operators as modifications to an out parameter candidate.
4+
- `FIO51-CPP` - `CloseFilesWhenTheyAreNoLongerNeeded.ql`:
5+
- Broadened definition of `IStream` and `OStream` types may result in reduced false negatives.
6+
- `A5-1-1` - `LiteralValueUsedOutsideTypeInit.ql`:
7+
- Broadened definition of `IStream` types may result in reduced false positives because more file stream function calls may be detected as logging operations that will be excluded from the results.

cpp/autosar/src/rules/A8-4-8/OutputParametersUsed.ql

+49-20
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,60 @@ import codingstandards.cpp.ConstHelpers
2323
import codingstandards.cpp.Operator
2424

2525
/**
26-
* Non-const T& and T* `Parameter`s to `Function`s
26+
* Holds if p is passed as a non-const reference or pointer and is modified.
27+
* This holds for in-out or out-only parameters.
2728
*/
28-
class NonConstReferenceOrPointerParameterCandidate extends FunctionParameter {
29-
NonConstReferenceOrPointerParameterCandidate() {
30-
this instanceof NonConstReferenceParameter
31-
or
32-
this instanceof NonConstPointerParameter
33-
}
29+
predicate isOutParameter(NonConstPointerorReferenceParameter p) {
30+
any(VariableEffect ve).getTarget() = p
31+
}
32+
33+
/**
34+
* Holds if parameter `p` is a parameter to a user defined assignment operator that
35+
* is defined outside of a class body.
36+
* These require an in-out parameter as the first argument.
37+
*/
38+
predicate isNonMemberUserAssignmentParameter(NonConstPointerorReferenceParameter p) {
39+
p.getFunction() instanceof UserAssignmentOperator and
40+
not p.isMember()
41+
}
42+
43+
/**
44+
* Holds if parameter `p` is a parameter to a stream insertion operator that
45+
* is defined outside of a class body.
46+
* These require an in-out parameter as the first argument.
47+
*
48+
* e.g., `std::ostream& operator<<(std::ostream& os, const T& obj)`
49+
*/
50+
predicate isStreamInsertionStreamParameter(NonConstPointerorReferenceParameter p) {
51+
exists(StreamInsertionOperator op | not op.isMember() | op.getParameter(0) = p)
3452
}
3553

36-
pragma[inline]
37-
predicate isFirstAccess(VariableAccess va) {
38-
not exists(VariableAccess otherVa |
39-
otherVa.getTarget() = va.getTarget() or
40-
otherVa.getQualifier().(VariableAccess).getTarget() = va.getTarget()
41-
|
42-
otherVa.getASuccessor() = va
54+
/**
55+
* Holds if parameter `p` is a parameter to a stream insertion operator that
56+
* is defined outside of a class body.
57+
* These require an in-out parameter as the first argument and an out parameter for the second.
58+
*
59+
* e.g., `std::istream& operator>>(std::istream& is, T& obj)`
60+
*/
61+
predicate isStreamExtractionParameter(NonConstPointerorReferenceParameter p) {
62+
exists(StreamExtractionOperator op | not op.isMember() |
63+
op.getParameter(0) = p
64+
or
65+
op.getParameter(1) = p
4366
)
4467
}
4568

46-
from NonConstReferenceOrPointerParameterCandidate p, VariableEffect ve
69+
predicate isException(NonConstPointerorReferenceParameter p) {
70+
isNonMemberUserAssignmentParameter(p) and p.getIndex() = 0
71+
or
72+
isStreamInsertionStreamParameter(p)
73+
or
74+
isStreamExtractionParameter(p)
75+
}
76+
77+
from NonConstPointerorReferenceParameter p
4778
where
4879
not isExcluded(p, ConstPackage::outputParametersUsedQuery()) and
49-
ve.getTarget() = p and
50-
isFirstAccess(ve.getAnAccess()) and
51-
not ve instanceof AnyAssignOperation and
52-
not ve instanceof CrementOperation
53-
select p, "Out parameter " + p.getName() + " that is modified before being read."
80+
isOutParameter(p) and
81+
not isException(p)
82+
select p, "Out parameter '" + p.getName() + "' used."
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
| test.cpp:5:22:5:24 | str | Out parameter str that is modified before being read. |
2-
| test.cpp:16:14:16:14 | i | Out parameter i that is modified before being read. |
3-
| test.cpp:21:14:21:14 | i | Out parameter i that is modified before being read. |
4-
| test.cpp:29:12:29:12 | a | Out parameter a that is modified before being read. |
5-
| test.cpp:33:12:33:12 | a | Out parameter a that is modified before being read. |
1+
| test.cpp:5:22:5:24 | str | Out parameter 'str' used. |
2+
| test.cpp:8:22:8:24 | str | Out parameter 'str' used. |
3+
| test.cpp:16:14:16:14 | i | Out parameter 'i' used. |
4+
| test.cpp:21:14:21:14 | i | Out parameter 'i' used. |
5+
| test.cpp:29:12:29:12 | a | Out parameter 'a' used. |
6+
| test.cpp:33:12:33:12 | a | Out parameter 'a' used. |

cpp/autosar/test/rules/A8-4-8/test.cpp

+39-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ void f(int &i) { // COMPLIANT
55
void f1(std::string &str) { // NON_COMPLIANT
66
str = "replacement";
77
}
8-
void f2(std::string &str) { // COMPLIANT
8+
void f2(std::string &str) { // NON_COMPLIANT
99
str += "suffix";
1010
}
1111

@@ -37,3 +37,41 @@ void f7(A &a) { // NON_COMPLIANT
3737
void f8(int i) { // COMPLIANT
3838
i += 1;
3939
}
40+
41+
constexpr A &operator|=(
42+
A &lhs,
43+
const A &rhs) noexcept { // COMPLIANT - non-member user defined assignment
44+
// operators are considered an exception.
45+
return lhs;
46+
}
47+
48+
enum class byte : unsigned char {};
49+
constexpr byte &
50+
operator|(const byte &lhs,
51+
const byte &rhs) noexcept { // COMPLIANT - parameters are const
52+
// qualified references
53+
return lhs | rhs;
54+
}
55+
constexpr byte &operator|=(
56+
byte &lhs,
57+
const byte rhs) noexcept { // COMPLIANT - non-member user defined assignment
58+
// operators are considered an exception.
59+
lhs = (lhs | rhs);
60+
return lhs;
61+
}
62+
63+
#include <iostream>
64+
std::ostream &operator<<(std::ostream &os,
65+
const byte &obj) { // COMPLIANT - insertion operators
66+
// are considered an exception.
67+
std::ostream other;
68+
os = other; // simulate modification
69+
return os;
70+
}
71+
72+
std::istream &operator>>(std::istream &is,
73+
byte &obj) { // COMPLIANT - extraction operators are
74+
// considered an exception.
75+
obj = static_cast<byte>('a'); // simulate modification
76+
return is;
77+
}

cpp/cert/src/rules/FIO51-CPP/CloseFilesWhenTheyAreNoLongerNeeded.ql

+4-2
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ predicate filebufAccess(ControlFlowNode node, FileStreamSource fss) {
3838
//insertion or extraction operator calls
3939
node.(InsertionOperatorCall).getFStream() = fss.getAUse() or
4040
node.(ExtractionOperatorCall).getFStream() = fss.getAUse() or
41-
//methods inherited from istream or ostream
42-
node.(IOStreamFunctionCall).getFStream() = fss.getAUse()
41+
// Methods inherited from istream or ostream that access the file stream.
42+
// Exclude is_open as it is not a filebuf access
43+
any(IOStreamFunctionCall call | node = call and not call.getTarget().hasName("is_open"))
44+
.getFStream() = fss.getAUse()
4345
}
4446

4547
/**

cpp/common/src/codingstandards/cpp/Operator.qll

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import cpp
22
import Expr
3+
private import semmle.code.cpp.security.FileWrite
4+
private import codingstandards.cpp.standardlibrary.FileStreams
35

46
/**
57
* any assignment operator that also reads from the access
@@ -119,14 +121,16 @@ class UserAssignmentOperator extends AssignmentOperator {
119121
}
120122

121123
/** An assignment operator of any sort */
122-
class AssignmentOperator extends MemberFunction {
124+
class AssignmentOperator extends Function {
123125
AssignmentOperator() {
124126
// operator op, where op is =, +=, -=, *=, /=, %=, ^=, &=, |=, >>=, <<=
125127
exists(string op |
126128
"operator" + op = this.getName() and
127129
op in ["=", "+=", "-=", "*=", "/=", "%=", "^=", "&=", "|=", ">>=", "<<="]
128130
)
129131
}
132+
133+
predicate isLValueRefQualified() { this.(MemberFunction).isLValueRefQualified() }
130134
}
131135

132136
class UserComparisonOperator extends Function {
@@ -265,6 +269,38 @@ class UserOverloadedOperator extends Function {
265269
}
266270
}
267271

272+
/** An implementation of a stream insertion operator. */
273+
class StreamInsertionOperator extends Function {
274+
StreamInsertionOperator() {
275+
this.hasName("operator<<") and
276+
(
277+
if this.isMember()
278+
then this.getNumberOfParameters() = 1
279+
else (
280+
this.getNumberOfParameters() = 2 and
281+
this.getParameter(0).getType() instanceof BasicOStreamClass
282+
)
283+
) and
284+
this.getType() instanceof BasicOStreamClass
285+
}
286+
}
287+
288+
/** An implementation of a stream extraction operator. */
289+
class StreamExtractionOperator extends Function {
290+
StreamExtractionOperator() {
291+
this.hasName("operator>>") and
292+
(
293+
if this.isMember()
294+
then this.getNumberOfParameters() = 1
295+
else (
296+
this.getNumberOfParameters() = 2 and
297+
this.getParameter(0).getType() instanceof IStream
298+
)
299+
) and
300+
this.getType() instanceof IStream
301+
}
302+
}
303+
268304
/** A user defined operator address of operator (`&`). */
269305
class UnaryAddressOfOperator extends Operator {
270306
UnaryAddressOfOperator() {

cpp/common/src/codingstandards/cpp/standardlibrary/FileStreams.qll

+23-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import cpp
1313
import codingstandards.cpp.dataflow.DataFlow
1414
import codingstandards.cpp.dataflow.TaintTracking
15+
private import codingstandards.cpp.Operator
1516

1617
/**
1718
* A `basic_fstream` like `std::fstream`
@@ -23,15 +24,31 @@ class FileStream extends ClassTemplateInstantiation {
2324
/**
2425
* A `basic_istream` like `std::istream`
2526
*/
26-
class IStream extends ClassTemplateInstantiation {
27-
IStream() { this.getTemplate().hasQualifiedName("std", "basic_istream") }
27+
class IStream extends Type {
28+
IStream() {
29+
this.(Class).getQualifiedName().matches("std::basic\\_istream%")
30+
or
31+
this.getUnspecifiedType() instanceof IStream
32+
or
33+
this.(Class).getABaseClass() instanceof IStream
34+
or
35+
this.(ReferenceType).getBaseType() instanceof IStream
36+
}
2837
}
2938

3039
/**
3140
* A `basic_ostream` like `std::ostream`
3241
*/
33-
class OStream extends ClassTemplateInstantiation {
34-
OStream() { this.getTemplate().hasQualifiedName("std", "basic_ostream") }
42+
class OStream extends Type {
43+
OStream() {
44+
this.(Class).getQualifiedName().matches("std::basic\\_ostream%")
45+
or
46+
this.getUnspecifiedType() instanceof OStream
47+
or
48+
this.(Class).getABaseClass() instanceof OStream
49+
or
50+
this.(ReferenceType).getBaseType() instanceof OStream
51+
}
3552
}
3653

3754
/**
@@ -53,7 +70,7 @@ predicate sameStreamSource(FileStreamFunctionCall a, FileStreamFunctionCall b) {
5370
* Insertion `operator<<` and Extraction `operator>>` operators.
5471
*/
5572
class InsertionOperatorCall extends FileStreamFunctionCall {
56-
InsertionOperatorCall() { this.getTarget().(Operator).hasQualifiedName("std", "operator<<") }
73+
InsertionOperatorCall() { this.getTarget() instanceof StreamInsertionOperator }
5774

5875
override Expr getFStream() {
5976
result = this.getQualifier()
@@ -63,7 +80,7 @@ class InsertionOperatorCall extends FileStreamFunctionCall {
6380
}
6481

6582
class ExtractionOperatorCall extends FileStreamFunctionCall {
66-
ExtractionOperatorCall() { this.getTarget().(Operator).hasQualifiedName("std", "operator>>") }
83+
ExtractionOperatorCall() { this.getTarget() instanceof StreamExtractionOperator }
6784

6885
override Expr getFStream() {
6986
result = this.getQualifier()

0 commit comments

Comments
 (0)