Skip to content

Commit 91d3f11

Browse files
authored
Merge pull request #121 from microsoft/flow-through-hashtables
PS: Flow through hash table creation, reads, and writes
2 parents 8aa119b + 2aacb58 commit 91d3f11

File tree

8 files changed

+128
-20
lines changed

8 files changed

+128
-20
lines changed
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
import powershell
22

3-
class BaseConstExpr extends @base_constant_expression, Expr { }
3+
/** The base class for constant expressions. */
4+
class BaseConstExpr extends @base_constant_expression, Expr {
5+
/** Gets the type of this constant expression. */
6+
string getType() { none() }
7+
8+
/** Gets a string literal of this constant expression. */
9+
StringLiteral getValue() { none() }
10+
11+
/** Gets a string literal representing this constant expression. */
12+
final override string toString() { result = this.getValue().toString() }
13+
}

powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import powershell
33
class ConstExpr extends @constant_expression, BaseConstExpr {
44
override SourceLocation getLocation() { constant_expression_location(this, result) }
55

6-
string getType() { constant_expression(this, result) }
6+
override string getType() { constant_expression(this, result) }
77

8-
StringLiteral getValue() { constant_expression_value(this, result) }
9-
10-
override string toString() { result = this.getValue().toString() }
8+
override StringLiteral getValue() { constant_expression_value(this, result) }
119
}
1210

1311
private newtype TConstantValue =
@@ -97,7 +95,7 @@ class ConstString extends ConstantValue, TConstString {
9795
result = "\"" + this.asString().replaceAll("\"", "\\\"") + "\""
9896
}
9997

100-
final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() }
98+
final override BaseConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() }
10199
}
102100

103101
/** A constant boolean value. */

powershell/ql/lib/semmle/code/powershell/HashTable.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ class HashTableExpr extends @hash_table, Expr {
55

66
final override string toString() { result = "${...}" }
77

8-
Stmt getExprWithKey(Expr key) { hash_table_key_value_pairs(this, _, key, result) } // TODO: Change @ast to @expr in db scheme
8+
Stmt getElement(Expr key) { hash_table_key_value_pairs(this, _, key, result) } // TODO: Change @ast to @expr in db scheme
99

10-
predicate hasKey(Expr key) { exists(this.getExprWithKey(key)) }
10+
predicate hasKey(Expr key) { exists(this.getElement(key)) }
1111

12-
Stmt getAnExpr() { result = this.getExprWithKey(_) }
12+
Stmt getAnElement() { result = this.getElement(_) }
1313

1414
predicate hasEntry(int index, Expr key, Stmt value) {
1515
hash_table_key_value_pairs(this, index, key, value)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import powershell
22

3+
/** A string constant. */
34
class StringConstExpr extends @string_constant_expression, BaseConstExpr {
4-
StringLiteral getValue() { string_constant_expression(this, result) }
5+
override StringLiteral getValue() { string_constant_expression(this, result) }
56

6-
/** Get the full string literal with all its parts concatenated */
7-
override string toString() { result = this.getValue().toString() }
7+
override string getType() { result = "String" }
88

99
override SourceLocation getLocation() { string_constant_expression_location(this, result) }
1010
}

powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,38 @@ module ExprNodes {
453453

454454
StmtBlockCfgNode getStmtBlock() { e.hasCfgChild(e.getStmtBlock(), this, result) }
455455
}
456+
457+
class HashTableChildMapping extends ExprChildMapping, HashTableExpr {
458+
override predicate relevantChild(Ast n) { this.hasEntry(_, _, n) or this.hasEntry(_, n, _) }
459+
}
460+
461+
class HashTableCfgNode extends ExprCfgNode {
462+
override string getAPrimaryQlClass() { result = "HashMapCfgNode" }
463+
464+
override HashTableChildMapping e;
465+
466+
override HashTableExpr getExpr() { result = super.getExpr() }
467+
468+
StmtCfgNode getElement(ExprCfgNode key) {
469+
exists(Expr eKey |
470+
eKey = key.getAstNode() and
471+
e.hasCfgChild(eKey, this, key) and
472+
e.hasCfgChild(e.getElement(eKey), this, result)
473+
)
474+
}
475+
476+
predicate hasKey(ExprCfgNode key) { exists(this.getElement(key)) }
477+
478+
StmtCfgNode getAnElement() { result = this.getElement(_) }
479+
480+
predicate hasEntry(int index, ExprCfgNode key, StmtCfgNode value) {
481+
exists(Expr eKey, Stmt sValue |
482+
e.hasCfgChild(eKey, this, key) and
483+
e.hasCfgChild(sValue, this, value) and
484+
e.hasEntry(index, eKey, sValue)
485+
)
486+
}
487+
}
456488
}
457489

458490
module StmtNodes {

powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPrivate.qll

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -660,10 +660,9 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
660660
node1.asStmt() = var.getAssignStmt().getRightHandSide() and
661661
e = var.getIndex()
662662
|
663-
exists(int index, Content::KnownElementContent ec |
663+
exists(Content::KnownElementContent ec |
664664
c.isKnownOrUnknownElement(ec) and
665-
index = ec.getIndex().asInt() and
666-
index = e.getValue().asInt()
665+
e.getValue() = ec.getIndex()
667666
)
668667
or
669668
not exists(e.getValue().asInt()) and
@@ -676,6 +675,18 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
676675
index = ec.getIndex().asInt()
677676
)
678677
or
678+
exists(CfgNodes::ExprCfgNode key |
679+
node2.asExpr().(CfgNodes::ExprNodes::HashTableCfgNode).getElement(key) = node1.asStmt()
680+
|
681+
exists(Content::KnownElementContent ec |
682+
c.isKnownOrUnknownElement(ec) and
683+
ec.getIndex() = key.getValue()
684+
)
685+
or
686+
not exists(key.getValue()) and
687+
c.isAnyElement()
688+
)
689+
or
679690
exists(
680691
CfgNodes::ExprNodes::ArrayExprCfgNode arrayExpr, EscapeContainer::EscapeContainer container
681692
|
@@ -714,13 +725,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
714725
node1.asExpr() = var.getBase() and
715726
e = var.getIndex()
716727
|
717-
exists(int index, Content::KnownElementContent ec |
728+
exists(Content::KnownElementContent ec |
718729
c.isKnownOrUnknownElement(ec) and
719-
index = ec.getIndex().asInt() and
720-
index = e.getValue().asInt()
730+
e.getValue() = ec.getIndex()
721731
)
722732
or
723-
not exists(e.getValue().asInt()) and
733+
not exists(e.getValue()) and
724734
c.isAnyElement()
725735
)
726736
or

powershell/ql/test/library-tests/dataflow/fields/test.expected

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,40 @@ edges
44
| test.ps1:1:8:1:18 | Source | test.ps1:1:1:1:3 | [post] a [f] | provenance | |
55
| test.ps1:2:6:2:8 | a [f] | test.ps1:2:6:2:10 | f | provenance | |
66
| test.ps1:8:1:8:6 | [post] arr1 [element 3] | test.ps1:9:6:9:11 | arr1 [element 3] | provenance | |
7+
| test.ps1:8:1:8:6 | [post] arr1 [element 3] | test.ps1:9:6:9:11 | arr1 [element 3] | provenance | |
8+
| test.ps1:8:12:8:22 | Source | test.ps1:8:1:8:6 | [post] arr1 [element 3] | provenance | |
79
| test.ps1:8:12:8:22 | Source | test.ps1:8:1:8:6 | [post] arr1 [element 3] | provenance | |
810
| test.ps1:9:6:9:11 | arr1 [element 3] | test.ps1:9:6:9:14 | ...[...] | provenance | |
11+
| test.ps1:9:6:9:11 | arr1 [element 3] | test.ps1:9:6:9:14 | ...[...] | provenance | |
912
| test.ps1:12:1:12:6 | [post] arr2 [element] | test.ps1:13:6:13:11 | arr2 [element] | provenance | |
1013
| test.ps1:12:19:12:29 | Source | test.ps1:12:1:12:6 | [post] arr2 [element] | provenance | |
1114
| test.ps1:13:6:13:11 | arr2 [element] | test.ps1:13:6:13:14 | ...[...] | provenance | |
1215
| test.ps1:15:1:15:6 | [post] arr3 [element 3] | test.ps1:16:6:16:11 | arr3 [element 3] | provenance | |
16+
| test.ps1:15:1:15:6 | [post] arr3 [element 3] | test.ps1:16:6:16:11 | arr3 [element 3] | provenance | |
17+
| test.ps1:15:12:15:22 | Source | test.ps1:15:1:15:6 | [post] arr3 [element 3] | provenance | |
1318
| test.ps1:15:12:15:22 | Source | test.ps1:15:1:15:6 | [post] arr3 [element 3] | provenance | |
1419
| test.ps1:16:6:16:11 | arr3 [element 3] | test.ps1:16:6:16:21 | ...[...] | provenance | |
20+
| test.ps1:16:6:16:11 | arr3 [element 3] | test.ps1:16:6:16:21 | ...[...] | provenance | |
1521
| test.ps1:18:1:18:6 | [post] arr4 [element] | test.ps1:19:6:19:11 | arr4 [element] | provenance | |
1622
| test.ps1:18:20:18:30 | Source | test.ps1:18:1:18:6 | [post] arr4 [element] | provenance | |
1723
| test.ps1:19:6:19:11 | arr4 [element] | test.ps1:19:6:19:22 | ...[...] | provenance | |
1824
| test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | test.ps1:22:6:22:11 | arr5 [element, element 1] | provenance | |
25+
| test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | test.ps1:22:6:22:11 | arr5 [element, element 1] | provenance | |
26+
| test.ps1:21:1:21:17 | [post] ...[...] [element 1] | test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | provenance | |
1927
| test.ps1:21:1:21:17 | [post] ...[...] [element 1] | test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | provenance | |
2028
| test.ps1:21:23:21:33 | Source | test.ps1:21:1:21:17 | [post] ...[...] [element 1] | provenance | |
29+
| test.ps1:21:23:21:33 | Source | test.ps1:21:1:21:17 | [post] ...[...] [element 1] | provenance | |
2130
| test.ps1:22:6:22:11 | arr5 [element, element 1] | test.ps1:22:6:22:22 | ...[...] [element 1] | provenance | |
31+
| test.ps1:22:6:22:11 | arr5 [element, element 1] | test.ps1:22:6:22:22 | ...[...] [element 1] | provenance | |
32+
| test.ps1:22:6:22:22 | ...[...] [element 1] | test.ps1:22:6:22:25 | ...[...] | provenance | |
2233
| test.ps1:22:6:22:22 | ...[...] [element 1] | test.ps1:22:6:22:25 | ...[...] | provenance | |
2334
| test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | test.ps1:26:6:26:11 | arr6 [element 1, element] | provenance | |
35+
| test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | test.ps1:26:6:26:11 | arr6 [element 1, element] | provenance | |
36+
| test.ps1:25:1:25:9 | [post] ...[...] [element] | test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | provenance | |
2437
| test.ps1:25:1:25:9 | [post] ...[...] [element] | test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | provenance | |
2538
| test.ps1:25:23:25:33 | Source | test.ps1:25:1:25:9 | [post] ...[...] [element] | provenance | |
2639
| test.ps1:26:6:26:11 | arr6 [element 1, element] | test.ps1:26:6:26:14 | ...[...] [element] | provenance | |
40+
| test.ps1:26:6:26:11 | arr6 [element 1, element] | test.ps1:26:6:26:14 | ...[...] [element] | provenance | |
2741
| test.ps1:26:6:26:14 | ...[...] [element] | test.ps1:26:6:26:25 | ...[...] | provenance | |
2842
| test.ps1:29:1:29:6 | [post] arr7 [element, element] | test.ps1:30:6:30:11 | arr7 [element, element] | provenance | |
2943
| test.ps1:29:1:29:6 | [post] arr7 [element, element] | test.ps1:31:6:31:11 | arr7 [element, element] | provenance | |
@@ -64,37 +78,55 @@ edges
6478
| test.ps1:72:6:72:8 | x [element] | test.ps1:72:6:72:11 | ...[...] | provenance | |
6579
| test.ps1:73:6:73:8 | x [element] | test.ps1:73:6:73:11 | ...[...] | provenance | |
6680
| test.ps1:74:6:74:8 | x [element] | test.ps1:74:6:74:11 | ...[...] | provenance | |
81+
| test.ps1:76:9:79:2 | ${...} [element a] | test.ps1:81:6:81:11 | hash [element a] | provenance | |
82+
| test.ps1:76:9:79:2 | ${...} [element a] | test.ps1:85:6:85:11 | hash [element a] | provenance | |
83+
| test.ps1:77:7:77:18 | Source | test.ps1:76:9:79:2 | ${...} [element a] | provenance | |
84+
| test.ps1:81:6:81:11 | hash [element a] | test.ps1:81:6:81:16 | ...[...] | provenance | |
85+
| test.ps1:85:6:85:11 | hash [element a] | test.ps1:85:6:85:16 | ...[...] | provenance | |
86+
| test.ps1:86:1:86:6 | [post] hash [b] | test.ps1:87:6:87:11 | hash [b] | provenance | |
87+
| test.ps1:86:11:86:22 | Source | test.ps1:86:1:86:6 | [post] hash [b] | provenance | |
88+
| test.ps1:87:6:87:11 | hash [b] | test.ps1:87:6:87:13 | b | provenance | |
6789
nodes
6890
| test.ps1:1:1:1:3 | [post] a [f] | semmle.label | [post] a [f] |
6991
| test.ps1:1:8:1:18 | Source | semmle.label | Source |
7092
| test.ps1:2:6:2:8 | a [f] | semmle.label | a [f] |
7193
| test.ps1:2:6:2:10 | f | semmle.label | f |
7294
| test.ps1:8:1:8:6 | [post] arr1 [element 3] | semmle.label | [post] arr1 [element 3] |
95+
| test.ps1:8:1:8:6 | [post] arr1 [element 3] | semmle.label | [post] arr1 [element 3] |
7396
| test.ps1:8:12:8:22 | Source | semmle.label | Source |
7497
| test.ps1:9:6:9:11 | arr1 [element 3] | semmle.label | arr1 [element 3] |
98+
| test.ps1:9:6:9:11 | arr1 [element 3] | semmle.label | arr1 [element 3] |
7599
| test.ps1:9:6:9:14 | ...[...] | semmle.label | ...[...] |
76100
| test.ps1:12:1:12:6 | [post] arr2 [element] | semmle.label | [post] arr2 [element] |
77101
| test.ps1:12:19:12:29 | Source | semmle.label | Source |
78102
| test.ps1:13:6:13:11 | arr2 [element] | semmle.label | arr2 [element] |
79103
| test.ps1:13:6:13:14 | ...[...] | semmle.label | ...[...] |
80104
| test.ps1:15:1:15:6 | [post] arr3 [element 3] | semmle.label | [post] arr3 [element 3] |
105+
| test.ps1:15:1:15:6 | [post] arr3 [element 3] | semmle.label | [post] arr3 [element 3] |
81106
| test.ps1:15:12:15:22 | Source | semmle.label | Source |
82107
| test.ps1:16:6:16:11 | arr3 [element 3] | semmle.label | arr3 [element 3] |
108+
| test.ps1:16:6:16:11 | arr3 [element 3] | semmle.label | arr3 [element 3] |
83109
| test.ps1:16:6:16:21 | ...[...] | semmle.label | ...[...] |
84110
| test.ps1:18:1:18:6 | [post] arr4 [element] | semmle.label | [post] arr4 [element] |
85111
| test.ps1:18:20:18:30 | Source | semmle.label | Source |
86112
| test.ps1:19:6:19:11 | arr4 [element] | semmle.label | arr4 [element] |
87113
| test.ps1:19:6:19:22 | ...[...] | semmle.label | ...[...] |
88114
| test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | semmle.label | [post] arr5 [element, element 1] |
115+
| test.ps1:21:1:21:6 | [post] arr5 [element, element 1] | semmle.label | [post] arr5 [element, element 1] |
116+
| test.ps1:21:1:21:17 | [post] ...[...] [element 1] | semmle.label | [post] ...[...] [element 1] |
89117
| test.ps1:21:1:21:17 | [post] ...[...] [element 1] | semmle.label | [post] ...[...] [element 1] |
90118
| test.ps1:21:23:21:33 | Source | semmle.label | Source |
91119
| test.ps1:22:6:22:11 | arr5 [element, element 1] | semmle.label | arr5 [element, element 1] |
120+
| test.ps1:22:6:22:11 | arr5 [element, element 1] | semmle.label | arr5 [element, element 1] |
121+
| test.ps1:22:6:22:22 | ...[...] [element 1] | semmle.label | ...[...] [element 1] |
92122
| test.ps1:22:6:22:22 | ...[...] [element 1] | semmle.label | ...[...] [element 1] |
93123
| test.ps1:22:6:22:25 | ...[...] | semmle.label | ...[...] |
94124
| test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | semmle.label | [post] arr6 [element 1, element] |
125+
| test.ps1:25:1:25:6 | [post] arr6 [element 1, element] | semmle.label | [post] arr6 [element 1, element] |
95126
| test.ps1:25:1:25:9 | [post] ...[...] [element] | semmle.label | [post] ...[...] [element] |
96127
| test.ps1:25:23:25:33 | Source | semmle.label | Source |
97128
| test.ps1:26:6:26:11 | arr6 [element 1, element] | semmle.label | arr6 [element 1, element] |
129+
| test.ps1:26:6:26:11 | arr6 [element 1, element] | semmle.label | arr6 [element 1, element] |
98130
| test.ps1:26:6:26:14 | ...[...] [element] | semmle.label | ...[...] [element] |
99131
| test.ps1:26:6:26:25 | ...[...] | semmle.label | ...[...] |
100132
| test.ps1:29:1:29:6 | [post] arr7 [element, element] | semmle.label | [post] arr7 [element, element] |
@@ -141,6 +173,16 @@ nodes
141173
| test.ps1:73:6:73:11 | ...[...] | semmle.label | ...[...] |
142174
| test.ps1:74:6:74:8 | x [element] | semmle.label | x [element] |
143175
| test.ps1:74:6:74:11 | ...[...] | semmle.label | ...[...] |
176+
| test.ps1:76:9:79:2 | ${...} [element a] | semmle.label | ${...} [element a] |
177+
| test.ps1:77:7:77:18 | Source | semmle.label | Source |
178+
| test.ps1:81:6:81:11 | hash [element a] | semmle.label | hash [element a] |
179+
| test.ps1:81:6:81:16 | ...[...] | semmle.label | ...[...] |
180+
| test.ps1:85:6:85:11 | hash [element a] | semmle.label | hash [element a] |
181+
| test.ps1:85:6:85:16 | ...[...] | semmle.label | ...[...] |
182+
| test.ps1:86:1:86:6 | [post] hash [b] | semmle.label | [post] hash [b] |
183+
| test.ps1:86:11:86:22 | Source | semmle.label | Source |
184+
| test.ps1:87:6:87:11 | hash [b] | semmle.label | hash [b] |
185+
| test.ps1:87:6:87:13 | b | semmle.label | b |
144186
subpaths
145187
testFailures
146188
#select
@@ -167,3 +209,6 @@ testFailures
167209
| test.ps1:74:6:74:11 | ...[...] | test.ps1:64:10:64:21 | Source | test.ps1:74:6:74:11 | ...[...] | $@ | test.ps1:64:10:64:21 | Source | Source |
168210
| test.ps1:74:6:74:11 | ...[...] | test.ps1:65:10:65:21 | Source | test.ps1:74:6:74:11 | ...[...] | $@ | test.ps1:65:10:65:21 | Source | Source |
169211
| test.ps1:74:6:74:11 | ...[...] | test.ps1:66:10:66:21 | Source | test.ps1:74:6:74:11 | ...[...] | $@ | test.ps1:66:10:66:21 | Source | Source |
212+
| test.ps1:81:6:81:16 | ...[...] | test.ps1:77:7:77:18 | Source | test.ps1:81:6:81:16 | ...[...] | $@ | test.ps1:77:7:77:18 | Source | Source |
213+
| test.ps1:85:6:85:16 | ...[...] | test.ps1:77:7:77:18 | Source | test.ps1:85:6:85:16 | ...[...] | $@ | test.ps1:77:7:77:18 | Source | Source |
214+
| test.ps1:87:6:87:13 | b | test.ps1:86:11:86:22 | Source | test.ps1:87:6:87:13 | b | $@ | test.ps1:86:11:86:22 | Source | Source |

powershell/ql/test/library-tests/dataflow/fields/test.ps1

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,17 @@ function produce {
7171
$x = produce
7272
Sink $x[0] # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15
7373
Sink $x[1] # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15
74-
Sink $x[2] # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15
74+
Sink $x[2] # $ hasValueFlow=13 hasValueFlow=14 hasValueFlow=15
75+
76+
$hash = @{
77+
a = Source "16"
78+
b = 2
79+
}
80+
81+
Sink $hash["a"] # $ hasValueFlow=16
82+
Sink $hash["b"] # clean
83+
84+
$hash["a"] = 0
85+
Sink $hash["a"] # $ SPURIOUS: hasValueFlow=16
86+
$hash.b = Source "17"
87+
Sink $hash.b # $ hasValueFlow=17

0 commit comments

Comments
 (0)