Skip to content

Commit 89c2b6d

Browse files
committed
Merge remote-tracking branch 'upstream/master' into split
2 parents a839f1f + e9a36b2 commit 89c2b6d

File tree

61 files changed

+3622
-1182
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3622
-1182
lines changed

change-notes/1.26/analysis-cpp.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ The following changes in version 1.26 affect C/C++ analysis in all applications.
1414
| **Query** | **Expected impact** | **Change** |
1515
|----------------------------|------------------------|------------------------------------------------------------------|
1616
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
17+
| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |
1718

1819
## Changes to libraries
1920

2021
* The models library now models more taint flows through `std::string`.
22+
* The `SimpleRangeAnalysis` library now supports multiplications of the form
23+
`e1 * e2` when `e1` and `e2` are unsigned.

cpp/ql/src/Metrics/Files/FNumberOfTests.ql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Expr getTest() {
1818
or
1919
// boost tests; http://www.boost.org/
2020
result.(FunctionCall).getTarget().hasQualifiedName("boost::unit_test", "make_test_case")
21+
or
22+
// googletest tests; https://github.com/google/googletest/
23+
result.(FunctionCall).getTarget().hasQualifiedName("testing::internal", "MakeAndRegisterTestInfo")
2124
}
2225

2326
from File f, int n

cpp/ql/src/Microsoft/SAL.qll

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
/**
2+
* Provides classes for identifying and reasoning about Microsoft source code
3+
* annotation language (SAL) macros.
4+
*/
5+
16
import cpp
27

8+
/**
9+
* A SAL macro defined in `sal.h` or a similar header file.
10+
*/
311
class SALMacro extends Macro {
412
SALMacro() {
513
exists(string filename | filename = this.getFile().getBaseName() |
@@ -20,27 +28,34 @@ class SALMacro extends Macro {
2028
}
2129

2230
pragma[noinline]
23-
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
31+
private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
2432

33+
/**
34+
* An invocation of a SAL macro (excluding invocations inside other macros).
35+
*/
2536
class SALAnnotation extends MacroInvocation {
2637
SALAnnotation() {
2738
this.getMacro() instanceof SALMacro and
2839
isTopLevelMacroAccess(this)
2940
}
3041

31-
/** Returns the `Declaration` annotated by `this`. */
42+
/** Gets the `Declaration` annotated by `this`. */
3243
Declaration getDeclaration() {
3344
annotatesAt(this, result.getADeclarationEntry(), _, _) and
3445
not result instanceof Type // exclude typedefs
3546
}
3647

37-
/** Returns the `DeclarationEntry` annotated by `this`. */
48+
/** Gets the `DeclarationEntry` annotated by `this`. */
3849
DeclarationEntry getDeclarationEntry() {
3950
annotatesAt(this, result, _, _) and
4051
not result instanceof TypeDeclarationEntry // exclude typedefs
4152
}
4253
}
4354

55+
/**
56+
* A SAL macro indicating that the return value of a function should always be
57+
* checked.
58+
*/
4459
class SALCheckReturn extends SALAnnotation {
4560
SALCheckReturn() {
4661
exists(SALMacro m | m = this.getMacro() |
@@ -50,6 +65,10 @@ class SALCheckReturn extends SALAnnotation {
5065
}
5166
}
5267

68+
/**
69+
* A SAL macro indicating that a pointer variable or return value should not be
70+
* `NULL`.
71+
*/
5372
class SALNotNull extends SALAnnotation {
5473
SALNotNull() {
5574
exists(SALMacro m | m = this.getMacro() |
@@ -69,6 +88,9 @@ class SALNotNull extends SALAnnotation {
6988
}
7089
}
7190

91+
/**
92+
* A SAL macro indicating that a value may be `NULL`.
93+
*/
7294
class SALMaybeNull extends SALAnnotation {
7395
SALMaybeNull() {
7496
exists(SALMacro m | m = this.getMacro() |
@@ -79,13 +101,29 @@ class SALMaybeNull extends SALAnnotation {
79101
}
80102
}
81103

104+
/**
105+
* A parameter annotated by one or more SAL annotations.
106+
*/
107+
class SALParameter extends Parameter {
108+
/** One of this parameter's annotations. */
109+
SALAnnotation a;
110+
111+
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
112+
113+
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
114+
115+
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
116+
117+
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
118+
}
119+
82120
///////////////////////////////////////////////////////////////////////////////
83121
// Implementation details
84122
/**
85123
* Holds if `a` annotates the declaration entry `d` and
86124
* its start position is the `idx`th position in `file` that holds a SAL element.
87125
*/
88-
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
126+
private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
89127
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
90128
}
91129

@@ -109,22 +147,6 @@ private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File
109147
)
110148
}
111149

112-
/**
113-
* A parameter annotated by one or more SAL annotations.
114-
*/
115-
class SALParameter extends Parameter {
116-
/** One of this parameter's annotations. */
117-
SALAnnotation a;
118-
119-
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
120-
121-
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
122-
123-
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
124-
125-
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
126-
}
127-
128150
/**
129151
* A SAL element, that is, a SAL annotation or a declaration entry
130152
* that may have SAL annotations.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the
7+
storage.</p>
8+
9+
</overview>
10+
<recommendation>
11+
12+
<p>Ensure that private data is always encrypted before being stored.
13+
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
14+
15+
<p>In general, decrypt private data only at the point where it is necessary for it to be used in
16+
cleartext.</p>
17+
18+
</recommendation>
19+
20+
<references>
21+
22+
<li><a href="https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A3-Sensitive_Data_Exposure">OWASP Sensitive_Data_Exposure</a></li>
23+
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
24+
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
25+
26+
27+
28+
<!-- LocalWords: CWE
29+
-->
30+
31+
</references>
32+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name Exposure of private information
3+
* @description If private information is written to an external location, it may be accessible by
4+
* unauthorized persons.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @id cpp/private-cleartext-write
8+
* @tags security
9+
* external/cwe/cwe-359
10+
*/
11+
12+
import cpp
13+
import experimental.semmle.code.cpp.security.PrivateCleartextWrite
14+
import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite
15+
import DataFlow::PathGraph
16+
17+
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
18+
where b.hasFlowPath(source, sink)
19+
select sink.getNode(),
20+
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
21+
source, "this source."
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
3+
*/
4+
5+
import cpp
6+
import semmle.code.cpp.dataflow.TaintTracking
7+
import experimental.semmle.code.cpp.security.PrivateData
8+
import semmle.code.cpp.security.FileWrite
9+
import semmle.code.cpp.security.BufferWrite
10+
import semmle.code.cpp.dataflow.TaintTracking
11+
12+
module PrivateCleartextWrite {
13+
/**
14+
* A data flow source for private information flowing unencrypted to an external location.
15+
*/
16+
abstract class Source extends DataFlow::ExprNode { }
17+
18+
/**
19+
* A data flow sink for private information flowing unencrypted to an external location.
20+
*/
21+
abstract class Sink extends DataFlow::ExprNode { }
22+
23+
/**
24+
* A sanitizer for private information flowing unencrypted to an external location.
25+
*/
26+
abstract class Sanitizer extends DataFlow::ExprNode { }
27+
28+
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
29+
class ProtectSanitizer extends Sanitizer {
30+
ProtectSanitizer() {
31+
exists(Function m, string s |
32+
this.getExpr().(FunctionCall).getTarget() = m and
33+
m.getName().regexpMatch("(?i).*" + s + ".*")
34+
|
35+
s = "protect" or s = "encode" or s = "encrypt"
36+
)
37+
}
38+
}
39+
40+
class WriteConfig extends TaintTracking::Configuration {
41+
WriteConfig() { this = "Write configuration" }
42+
43+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
44+
45+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
46+
47+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
48+
}
49+
50+
class PrivateDataSource extends Source {
51+
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
52+
}
53+
54+
class WriteSink extends Sink {
55+
WriteSink() {
56+
exists(FileWrite f, BufferWrite b |
57+
this.asExpr() = f.getASource()
58+
or
59+
this.asExpr() = b.getAChild()
60+
)
61+
}
62+
}
63+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Provides classes and predicates for identifying private data and functions for security.
3+
*
4+
* 'Private' data in general is anything that would compromise user privacy if exposed. This
5+
* library tries to guess where private data may either be stored in a variable or produced by a
6+
* function.
7+
*
8+
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
9+
* to credentials.
10+
*/
11+
12+
import cpp
13+
14+
/** A string for `match` that identifies strings that look like they represent private data. */
15+
private string privateNames() {
16+
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
17+
// Government identifiers, such as Social Security Numbers
18+
result = "%social%security%number%" or
19+
// Contact information, such as home addresses and telephone numbers
20+
result = "%postcode%" or
21+
result = "%zipcode%" or
22+
// result = "%telephone%" or
23+
// Geographic location - where the user is (or was)
24+
result = "%latitude%" or
25+
result = "%longitude%" or
26+
// Financial data - such as credit card numbers, salary, bank accounts, and debts
27+
result = "%creditcard%" or
28+
result = "%salary%" or
29+
result = "%bankaccount%" or
30+
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
31+
// result = "%email%" or
32+
// result = "%mobile%" or
33+
result = "%employer%" or
34+
// Health - medical conditions, insurance status, prescription records
35+
result = "%medical%"
36+
}
37+
38+
/** An expression that might contain private data. */
39+
abstract class PrivateDataExpr extends Expr { }
40+
41+
/** A functiond call that might produce private data. */
42+
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
43+
PrivateFunctionCall() {
44+
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
45+
}
46+
}
47+
48+
/** An access to a variable that might contain private data. */
49+
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
50+
PrivateVariableAccess() {
51+
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
52+
}
53+
}

cpp/ql/src/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,10 @@ float safeFloor(float v) {
156156
result = v
157157
}
158158

159+
private class UnsignedMulExpr extends MulExpr {
160+
UnsignedMulExpr() { this.getType().(IntegralType).isUnsigned() }
161+
}
162+
159163
/** Set of expressions which we know how to analyze. */
160164
private predicate analyzableExpr(Expr e) {
161165
// The type of the expression must be arithmetic. We reuse the logic in
@@ -178,6 +182,8 @@ private predicate analyzableExpr(Expr e) {
178182
or
179183
e instanceof SubExpr
180184
or
185+
e instanceof UnsignedMulExpr
186+
or
181187
e instanceof AssignExpr
182188
or
183189
e instanceof AssignAddExpr
@@ -278,6 +284,10 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
278284
or
279285
exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar))
280286
or
287+
exists(UnsignedMulExpr mulExpr | e = mulExpr |
288+
exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
289+
)
290+
or
281291
exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar))
282292
or
283293
exists(AssignAddExpr addExpr | e = addExpr |
@@ -625,6 +635,13 @@ private float getLowerBoundsImpl(Expr expr) {
625635
result = addRoundingDown(xLow, -yHigh)
626636
)
627637
or
638+
exists(UnsignedMulExpr mulExpr, float xLow, float yLow |
639+
expr = mulExpr and
640+
xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and
641+
yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and
642+
result = xLow * yLow
643+
)
644+
or
628645
exists(AssignExpr assign |
629646
expr = assign and
630647
result = getFullyConvertedLowerBounds(assign.getRValue())
@@ -794,6 +811,13 @@ private float getUpperBoundsImpl(Expr expr) {
794811
result = addRoundingUp(xHigh, -yLow)
795812
)
796813
or
814+
exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh |
815+
expr = mulExpr and
816+
xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and
817+
yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and
818+
result = xHigh * yHigh
819+
)
820+
or
797821
exists(AssignExpr assign |
798822
expr = assign and
799823
result = getFullyConvertedUpperBounds(assign.getRValue())

0 commit comments

Comments
 (0)