Skip to content

Commit 8c460ae

Browse files
author
Max Schaefer
committed
Merge remote-tracking branch 'upstream/master' into rc/1.20-merge-master
Conflict in `javascript/extractor/src/com/semmle/js/extractor/Main.java` resolved in favour of `master`.
2 parents fb499b0 + 313134c commit 8c460ae

File tree

163 files changed

+2763
-424
lines changed

Some content is hidden

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

163 files changed

+2763
-424
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@
4747
*.jpeg -text
4848
*.gif -text
4949
*.dll -text
50+
*.pdb -text
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Improvements to JavaScript analysis
2+
3+
## General improvements
4+
5+
* Support for the following frameworks and libraries has been improved:
6+
- [socket.io](http://socket.io)
7+
8+
* The security queries now track data flow through Base64 decoders such as the Node.js `Buffer` class, the DOM function `atob`, and a number of npm packages intcluding [`abab`](https://www.npmjs.com/package/abab), [`atob`](https://www.npmjs.com/package/atob), [`btoa`](https://www.npmjs.com/package/btoa), [`base-64`](https://www.npmjs.com/package/base-64), [`js-base64`](https://www.npmjs.com/package/js-base64), [`Base64.js`](https://www.npmjs.com/package/Base64) and [`base64-js`](https://www.npmjs.com/package/base64-js).
9+
10+
11+
## New queries
12+
13+
| **Query** | **Tags** | **Purpose** |
14+
|-----------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
15+
16+
## Changes to existing queries
17+
18+
| **Query** | **Expected impact** | **Change** |
19+
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
20+
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
21+
| Useless assignment to property | Fewer false-positive results | This rule now ignore reads of additional getters. |
22+
| Arbitrary file write during zip extraction ("Zip Slip") | More results | This rule now considers more libraries, including tar as well as zip. |
23+
24+
## Changes to QL libraries

cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.qhelp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<overview>
88
<p>This rule finds classes with more than 15 instance (i.e., non-<code>static</code>) fields. Library classes
9-
are not shown. Having too many fields in one class is a sign that the class lacks cohesion (i.e. lacks a single purpose).
9+
are not shown. Having too many fields in one class is a sign that the class may lack cohesion (i.e. lack a single purpose).
1010
These classes can be split into smaller, more cohesive classes. Alternatively, the related fields can be grouped
1111
into <code>struct</code>s.</p>
1212

cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyFields.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,5 +110,5 @@ where n = strictcount(string fieldName
110110
not c.isConstructedFrom(_) and
111111
c = vdg.getClass() and
112112
if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@"
113-
select c, kindstr(c) + " " + c.getName() + " has " + n + " fields, which is too many" + suffix + ".",
113+
select c, kindstr(c) + " " + c.getName() + " has " + n + " fields; we suggest refactoring to 15 fields or fewer" + suffix + ".",
114114
vdg, vdg.describeGroup()

cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* correctness
99
* external/cwe/cwe-770
1010
*/
11+
1112
import cpp
1213

1314
Loop getAnEnclosingLoopOfExpr(Expr e) {
@@ -21,7 +22,15 @@ Loop getAnEnclosingLoopOfStmt(Stmt s) {
2122
}
2223

2324
from Loop l, FunctionCall fc
24-
where getAnEnclosingLoopOfExpr(fc) = l
25-
and fc.getTarget().getName() = "__builtin_alloca"
26-
and not l.(DoStmt).getCondition().getValue() = "0"
27-
select fc, "Stack allocation is inside a $@ and could lead to overflow.", l, l.toString()
25+
where
26+
getAnEnclosingLoopOfExpr(fc) = l and
27+
(
28+
fc.getTarget().getName() = "__builtin_alloca"
29+
or
30+
(
31+
(fc.getTarget().getName() = "_alloca" or fc.getTarget().getName() = "_malloca") and
32+
fc.getTarget().getADeclarationEntry().getFile().getBaseName() = "malloc.h"
33+
)
34+
) and
35+
not l.(DoStmt).getCondition().getValue() = "0"
36+
select fc, "Stack allocation is inside a $@ and could lead to stack overflow.", l, l.toString()

cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ where
5656
or
5757
// The data flow library doesn't support conversions, so here we check that
5858
// the address escapes into some expression `pointerToLocal`, which flows
59-
// in a one or more steps to a returned expression.
59+
// in one or more steps to a returned expression.
6060
exists(Expr pointerToLocal |
6161
variableAddressEscapesTree(va, pointerToLocal.getFullyConverted()) and
6262
not hasNontrivialConversion(pointerToLocal) and

cpp/ql/src/Security/CWE/CWE-327/BrokenCryptoAlgorithm.ql

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ abstract class InsecureCryptoSpec extends Locatable {
1616
abstract string description();
1717
}
1818

19+
Function getAnInsecureFunction() {
20+
result.getName().regexpMatch(algorithmBlacklistRegex()) and
21+
exists(result.getACallToThisFunction())
22+
}
23+
1924
class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall {
2025
InsecureFunctionCall() {
21-
this.getTarget().getName().regexpMatch(algorithmBlacklistRegex())
26+
this.getTarget() = getAnInsecureFunction()
2227
}
2328

2429
override string description() { result = "function call" }
@@ -27,9 +32,14 @@ class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall {
2732
override Location getLocation() { result = FunctionCall.super.getLocation() }
2833
}
2934

35+
Macro getAnInsecureMacro() {
36+
result.getName().regexpMatch(algorithmBlacklistRegex()) and
37+
exists(result.getAnInvocation())
38+
}
39+
3040
class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation {
3141
InsecureMacroSpec() {
32-
this.getMacro().getName().regexpMatch(algorithmBlacklistRegex())
42+
this.getMacro() = getAnInsecureMacro()
3343
}
3444

3545
override string description() { result = "macro invocation" }

cpp/ql/src/semmle/code/cpp/commons/Printf.qll

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,9 @@ class FormatLiteral extends Literal {
215215
/**
216216
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
217217
* a `char *` (either way, `%S` will have the opposite meaning).
218+
* DEPRECATED: Use getDefaultCharType() instead.
218219
*/
219-
predicate isWideCharDefault() {
220+
deprecated predicate isWideCharDefault() {
220221
getUse().getTarget().(FormattingFunction).isWideCharDefault()
221222
}
222223

@@ -671,37 +672,34 @@ class FormatLiteral extends Literal {
671672
}
672673

673674
/**
674-
* Gets the 'effective' char type character, that is, 'c' (meaning a `char`) or
675-
* 'C' (meaning a `wchar_t`).
676-
* - in the base case this is the same as the format type character.
677-
* - for a `wprintf` or similar function call, the meanings are reversed.
678-
* - the size prefixes 'l'/'w' (long) and 'h' (short) override the
679-
* type character to effectively 'C' or 'c' respectively.
675+
* Gets the char type required by the nth conversion specifier.
676+
* - in the base case this is the default for the formatting function
677+
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
678+
* - the `%S` format character reverses wideness.
679+
* - the size prefixes 'l'/'w' and 'h' override the type character
680+
* to wide or single-byte characters respectively.
680681
*/
681-
private string getEffectiveCharConversionChar(int n) {
682-
exists(string len, string conv | this.parseConvSpec(n, _, _, _, _, _, len, conv) and (conv = "c" or conv = "C") |
683-
(len = "l" and result = "C") or
684-
(len = "w" and result = "C") or
685-
(len = "h" and result = "c") or
686-
(len != "l" and len != "w" and len != "h" and (result = "c" or result = "C") and (if isWideCharDefault() then result != conv else result = conv))
687-
)
688-
}
689-
690682
private Type getConversionType1b(int n) {
691-
exists(string cnv | cnv = this.getEffectiveCharConversionChar(n) |
683+
exists(string len, string conv |
684+
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
692685
(
693-
cnv = "c" and
694-
result instanceof CharType and
695-
not result.(CharType).isExplicitlySigned() and
696-
not result.(CharType).isExplicitlyUnsigned()
697-
) or (
698-
cnv = "C" and
699-
isMicrosoft() and
700-
result instanceof WideCharType
701-
) or (
702-
cnv = "C" and
703-
not isMicrosoft() and
704-
result.hasName("wint_t")
686+
(
687+
(conv = "c" or conv = "C") and
688+
len = "h" and
689+
result instanceof PlainCharType
690+
) or (
691+
(conv = "c" or conv = "C") and
692+
(len = "l" or len = "w") and
693+
result = getWideCharType()
694+
) or (
695+
conv = "c" and
696+
(len != "l" and len != "w" and len != "h") and
697+
result = getDefaultCharType()
698+
) or (
699+
conv = "C" and
700+
(len != "l" and len != "w" and len != "h") and
701+
result = getNonDefaultCharType()
702+
)
705703
)
706704
)
707705
}
@@ -846,15 +844,7 @@ class FormatLiteral extends Literal {
846844
len = 1
847845
or (
848846
this.getConversionChar(n).toLowerCase()="c" and
849-
if (this.getEffectiveCharConversionChar(n)="C" and
850-
not isMicrosoft() and
851-
not isWideCharDefault()) then (
852-
len = 6 // MB_LEN_MAX
853-
// the wint_t (wide character) argument is converted
854-
// to a multibyte sequence by a call to the wcrtomb(3)
855-
) else (
856-
len = 1 // e.g. 'a'
857-
)
847+
len = 1 // e.g. 'a'
858848
) or this.getConversionChar(n).toLowerCase()="f" and
859849
exists(int dot, int afterdot |
860850
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1)

cpp/ql/src/semmle/code/cpp/controlflow/SSAUtils.qll

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,19 @@ import semmle.code.cpp.controlflow.SSA // must be imported for proper caching of
44
import semmle.code.cpp.rangeanalysis.RangeSSA // must be imported for proper caching of SSAHelper
55

66
/* The dominance frontier of a block `x` is the set of all blocks `w` such that
7-
* `x` dominates a predecessor of `w` but does not strictly dominate `w`. */
8-
pragma[noinline]
7+
* `x` dominates a predecessor of `w` but does not strictly dominate `w`.
8+
*
9+
* This implementation is equivalent to:
10+
*
11+
* bbDominates(x, w.getAPredecessor()) and not bbStrictlyDominates(x, w)
12+
*/
913
private predicate dominanceFrontier(BasicBlock x, BasicBlock w) {
10-
bbDominates(x, w.getAPredecessor()) and not bbStrictlyDominates(x, w)
14+
x = w.getAPredecessor() and not bbIDominates(x, w)
15+
or
16+
exists(BasicBlock prev | dominanceFrontier(prev, w) |
17+
bbIDominates(x, prev) and
18+
not bbIDominates(x, w)
19+
)
1120
}
1221

1322
/**

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowDispatch.qll

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,59 @@ private import cpp
22
private import DataFlowPrivate
33

44
Function viableImpl(MethodAccess ma) {
5-
result = ma.getTarget()
5+
result = viableCallable(ma)
66
}
77

8+
/**
9+
* Gets a function that might be called by `call`.
10+
*/
811
Function viableCallable(Call call) {
912
result = call.getTarget()
13+
or
14+
// If the target of the call does not have a body in the snapshot, it might
15+
// be because the target is just a header declaration, and the real target
16+
// will be determined at run time when the caller and callee are linked
17+
// together by the operating system's dynamic linker. In case a _unique_
18+
// function with the right signature is present in the database, we return
19+
// that as a potential callee.
20+
exists(string qualifiedName, int nparams |
21+
callSignatureWithoutBody(qualifiedName, nparams, call) and
22+
functionSignatureWithBody(qualifiedName, nparams, result) and
23+
strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1
24+
)
25+
}
26+
27+
/**
28+
* Holds if `f` is a function with a body that has name `qualifiedName` and
29+
* `nparams` parameter count. See `functionSignature`.
30+
*/
31+
private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) {
32+
functionSignature(f, qualifiedName, nparams) and
33+
exists(f.getBlock())
34+
}
35+
36+
/**
37+
* Holds if the target of `call` is a function _with no definition_ that has
38+
* name `qualifiedName` and `nparams` parameter count. See `functionSignature`.
39+
*/
40+
pragma[noinline]
41+
private predicate callSignatureWithoutBody(string qualifiedName, int nparams, Call call) {
42+
exists(Function target |
43+
target = call.getTarget() and
44+
not exists(target.getBlock()) and
45+
functionSignature(target, qualifiedName, nparams)
46+
)
47+
}
48+
49+
/**
50+
* Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is
51+
* an approximation of its signature for the purpose of matching functions that
52+
* might be the same across link targets.
53+
*/
54+
private predicate functionSignature(Function f, string qualifiedName, int nparams) {
55+
qualifiedName = f.getQualifiedName() and
56+
nparams = f.getNumberOfParameters() and
57+
not f.isStatic()
1058
}
1159

1260
/**

0 commit comments

Comments
 (0)