Skip to content

Commit 3231b60

Browse files
authored
Merge pull request #1272 from asger-semmle/access-path-capture
Approved by xiemaisi
2 parents 47ba7d3 + a16753c commit 3231b60

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

javascript/ql/src/semmle/javascript/dataflow/internal/AccessPaths.qll

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,38 @@ private SsaVariable getRefinedVariable(SsaVariable variable) {
4343
result = variable.getDefinition().(SsaRefinementNode).getAnInput()
4444
}
4545

46-
private SsaVariable getARefinementOf(SsaVariable variable) {
47-
variable = getRefinedVariable(result)
48-
}
46+
private SsaVariable getARefinementOf(SsaVariable variable) { variable = getRefinedVariable(result) }
4947

5048
/**
51-
* A representation of a (nested) property access on an SSA variable
49+
* A representation of a (nested) property access on an SSA variable or captured variable
5250
* where each property name is either constant or itself an SSA variable.
5351
*/
5452
private newtype TAccessPath =
53+
/**
54+
* An access path rooted in an SSA variable.
55+
*
56+
* Refinement nodes are treated as no-ops so that all uses of a refined value are
57+
* given the same access path. Refinement nodes are therefore never treated as roots.
58+
*/
5559
MkSsaRoot(SsaVariable var) {
56-
not exists(getRefinedVariable(var))
57-
}
58-
or
60+
not exists(getRefinedVariable(var)) and
61+
not var.getSourceVariable().isCaptured() // Handled by MkCapturedRoot
62+
} or
63+
/**
64+
* An access path rooted in a captured variable.
65+
*
66+
* The SSA form for captured variables is too conservative for constructing
67+
* access paths across function boundaries, so in this case we use the source
68+
* variable as the root.
69+
*/
70+
MkCapturedRoot(LocalVariable var) { var.isCaptured() } or
71+
/**
72+
* An access path rooted in the receiver of a function.
73+
*/
5974
MkThisRoot(Function function) { function.getThisBinder() = function } or
75+
/**
76+
* A property access on an access path.
77+
*/
6078
MkAccessStep(AccessPath base, PropertyName name) {
6179
exists(PropAccess pacc |
6280
pacc.getBase() = base.getAnInstance() and
@@ -65,7 +83,7 @@ private newtype TAccessPath =
6583
}
6684

6785
/**
68-
* A representation of a (nested) property access on an SSA variable
86+
* A representation of a (nested) property access on an SSA variable or captured variable
6987
* where each property name is either constant or itself an SSA variable.
7088
*/
7189
class AccessPath extends TAccessPath {
@@ -78,6 +96,12 @@ class AccessPath extends TAccessPath {
7896
result = getARefinementOf*(var).getAUseIn(bb)
7997
)
8098
or
99+
exists(Variable var |
100+
this = MkCapturedRoot(var) and
101+
result = var.getAnAccess() and
102+
result.getBasicBlock() = bb
103+
)
104+
or
81105
exists(ThisExpr this_ |
82106
this = MkThisRoot(this_.getBinder()) and
83107
result = this_ and

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
| callbacks.js:44:17:44:24 | source() | callbacks.js:41:10:41:10 | x |
1313
| callbacks.js:50:18:50:25 | source() | callbacks.js:30:29:30:29 | y |
1414
| callbacks.js:51:18:51:25 | source() | callbacks.js:30:29:30:29 | y |
15+
| captured-sanitizer.js:25:3:25:10 | source() | captured-sanitizer.js:15:10:15:10 | x |
1516
| closure.js:6:15:6:22 | source() | closure.js:8:8:8:31 | string. ... (taint) |
1617
| closure.js:6:15:6:22 | source() | closure.js:9:8:9:25 | string.trim(taint) |
1718
| closure.js:6:15:6:22 | source() | closure.js:10:8:10:33 | string. ... nt, 50) |
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import * as dummy from 'dummy';
2+
3+
function f(x) {
4+
useVar();
5+
useVar();
6+
mutateVar();
7+
mutateVar();
8+
9+
function useVar() {
10+
if (isSafe(x)) {
11+
causeReCapture();
12+
causeReCapture();
13+
sink(x); // OK
14+
}
15+
sink(x); // NOT OK
16+
}
17+
18+
function causeReCapture() {}
19+
20+
function mutateVar() {
21+
x = null;
22+
}
23+
}
24+
25+
f(source());

0 commit comments

Comments
 (0)