Skip to content

Commit bf771a1

Browse files
authored
Merge pull request github#13563 from jketema/clears-content
C++: Implement `clearsContent` for IR dataflow
2 parents a7c2a25 + b1ae3a0 commit bf771a1

File tree

14 files changed

+940
-55
lines changed

14 files changed

+940
-55
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -651,13 +651,16 @@ predicate jumpStep(Node n1, Node n2) {
651651
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
652652
* Thus, `node2` references an object with a field `f` that contains the
653653
* value of `node1`.
654+
*
655+
* The boolean `certain` is true if the destination address does not involve
656+
* any pointer arithmetic, and false otherwise.
654657
*/
655-
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
658+
predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) {
656659
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
657660
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
658661
node2.getIndirectionIndex() = 1 and
659662
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
660-
numberOfLoads)
663+
numberOfLoads, certain)
661664
|
662665
exists(FieldContent fc | fc = c |
663666
fc.getField() = node2.getUpdatedField() and
@@ -671,35 +674,51 @@ predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
671674
)
672675
}
673676

677+
/**
678+
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
679+
* Thus, `node2` references an object with a field `f` that contains the
680+
* value of `node1`.
681+
*/
682+
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
683+
storeStepImpl(node1, c, node2, _)
684+
}
685+
674686
/**
675687
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
676688
* operations and exactly `n` `LoadInstruction` operations.
677689
*/
678-
private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) {
690+
private predicate numberOfLoadsFromOperandRec(
691+
Operand operandFrom, Operand operandTo, int ind, boolean certain
692+
) {
679693
exists(Instruction load | Ssa::isDereference(load, operandFrom) |
680-
operandTo = operandFrom and ind = 0
694+
operandTo = operandFrom and ind = 0 and certain = true
681695
or
682-
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1)
696+
numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1, certain)
683697
)
684698
or
685-
exists(Operand op, Instruction instr |
699+
exists(Operand op, Instruction instr, boolean isPointerArith, boolean certain0 |
686700
instr = op.getDef() and
687-
conversionFlow(operandFrom, instr, _, _) and
688-
numberOfLoadsFromOperand(op, operandTo, ind)
701+
conversionFlow(operandFrom, instr, isPointerArith, _) and
702+
numberOfLoadsFromOperand(op, operandTo, ind, certain0)
703+
|
704+
if isPointerArith = true then certain = false else certain = certain0
689705
)
690706
}
691707

692708
/**
693709
* Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like
694710
* operations and exactly `n` `LoadInstruction` operations.
695711
*/
696-
private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) {
697-
numberOfLoadsFromOperandRec(operandFrom, operandTo, n)
712+
private predicate numberOfLoadsFromOperand(
713+
Operand operandFrom, Operand operandTo, int n, boolean certain
714+
) {
715+
numberOfLoadsFromOperandRec(operandFrom, operandTo, n, certain)
698716
or
699717
not Ssa::isDereference(_, operandFrom) and
700718
not conversionFlow(operandFrom, _, _, _) and
701719
operandFrom = operandTo and
702-
n = 0
720+
n = 0 and
721+
certain = true
703722
}
704723

705724
// Needed to join on both an operand and an index at the same time.
@@ -729,7 +748,7 @@ predicate readStep(Node node1, Content c, Node node2) {
729748
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
730749
// in `storeStep`.
731750
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
732-
numberOfLoadsFromOperand(fa1, operand, numberOfLoads)
751+
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _)
733752
|
734753
exists(FieldContent fc | fc = c |
735754
fc.getField() = fa1.getField() and
@@ -747,7 +766,33 @@ predicate readStep(Node node1, Content c, Node node2) {
747766
* Holds if values stored inside content `c` are cleared at node `n`.
748767
*/
749768
predicate clearsContent(Node n, Content c) {
750-
none() // stub implementation
769+
n =
770+
any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun)
771+
.getPreUpdateNode() and
772+
(
773+
// The crement operations and pointer addition and subtraction self-assign. We do not
774+
// want to clear the contents if it is indirectly pointed at by any of these operations,
775+
// as part of the contents might still be accessible afterwards. If there is no such
776+
// indirection clearing the contents is safe.
777+
not exists(Operand op, Cpp::Operation p |
778+
n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and
779+
(
780+
p instanceof Cpp::AssignPointerAddExpr or
781+
p instanceof Cpp::AssignPointerSubExpr or
782+
p instanceof Cpp::CrementOperation
783+
)
784+
|
785+
p.getAnOperand() = op.getUse().getAst()
786+
)
787+
or
788+
forex(PostUpdateNode pun, Content d |
789+
pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and
790+
storeStepImpl(_, d, pun, true) and
791+
pun.getPreUpdateNode() = n
792+
|
793+
c.getIndirectionIndex() = d.getIndirectionIndex()
794+
)
795+
)
751796
}
752797

753798
/**

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,20 @@ class Content extends TContent {
18321832
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
18331833
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
18341834
}
1835+
1836+
/** Gets the indirection index of this `Content`. */
1837+
abstract int getIndirectionIndex();
1838+
1839+
/**
1840+
* INTERNAL: Do not use.
1841+
*
1842+
* Holds if a write to this `Content` implies that `c` is
1843+
* also cleared.
1844+
*
1845+
* For example, a write to a field `f` implies that any content of
1846+
* the form `*f` is also cleared.
1847+
*/
1848+
abstract predicate impliesClearOf(Content c);
18351849
}
18361850

18371851
/** A reference through a non-union instance field. */
@@ -1849,10 +1863,21 @@ class FieldContent extends Content, TFieldContent {
18491863

18501864
Field getField() { result = f }
18511865

1866+
/** Gets the indirection index of this `FieldContent`. */
18521867
pragma[inline]
1853-
int getIndirectionIndex() {
1868+
override int getIndirectionIndex() {
18541869
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
18551870
}
1871+
1872+
override predicate impliesClearOf(Content c) {
1873+
exists(FieldContent fc |
1874+
fc = c and
1875+
fc.getField() = f and
1876+
// If `this` is `f` then `c` is cleared if it's of the
1877+
// form `*f`, `**f`, etc.
1878+
fc.getIndirectionIndex() >= indirectionIndex
1879+
)
1880+
}
18561881
}
18571882

18581883
/** A reference through an instance field of a union. */
@@ -1877,9 +1902,21 @@ class UnionContent extends Content, TUnionContent {
18771902

18781903
/** Gets the indirection index of this `UnionContent`. */
18791904
pragma[inline]
1880-
int getIndirectionIndex() {
1905+
override int getIndirectionIndex() {
18811906
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
18821907
}
1908+
1909+
override predicate impliesClearOf(Content c) {
1910+
exists(UnionContent uc |
1911+
uc = c and
1912+
uc.getUnion() = u and
1913+
// If `this` is `u` then `c` is cleared if it's of the
1914+
// form `*u`, `**u`, etc. (and we ignore `bytes` because
1915+
// we know the entire union is overwritten because it's a
1916+
// union).
1917+
uc.getIndirectionIndex() >= indirectionIndex
1918+
)
1919+
}
18831920
}
18841921

18851922
/**

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ edges
44
| test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:28:19:28:26 | call to mk_array [p] |
55
| test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:50:18:50:25 | call to mk_array [p] |
66
| test.cpp:21:5:21:24 | ... = ... | test.cpp:21:9:21:9 | arr indirection [post update] [p] |
7-
| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:19:9:19:16 | mk_array indirection [p] |
7+
| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:22:5:22:7 | arr indirection [p] |
88
| test.cpp:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... |
9+
| test.cpp:22:5:22:7 | arr indirection [p] | test.cpp:19:9:19:16 | mk_array indirection [p] |
910
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | arr indirection [p] |
1011
| test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | arr indirection [p] |
1112
| test.cpp:31:9:31:11 | arr indirection [p] | test.cpp:31:13:31:13 | p indirection |
@@ -20,18 +21,20 @@ edges
2021
| test.cpp:45:13:45:13 | p indirection | test.cpp:45:13:45:13 | p |
2122
| test.cpp:50:18:50:25 | call to mk_array [p] | test.cpp:39:27:39:29 | arr [p] |
2223
| test.cpp:55:5:55:24 | ... = ... | test.cpp:55:9:55:9 | arr indirection [post update] [p] |
23-
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:59:9:59:11 | arr indirection [p] |
24-
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:63:9:63:11 | arr indirection [p] |
24+
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:56:5:56:7 | arr indirection [p] |
2525
| test.cpp:55:13:55:18 | call to malloc | test.cpp:55:5:55:24 | ... = ... |
26+
| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:59:9:59:11 | arr indirection [p] |
27+
| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:63:9:63:11 | arr indirection [p] |
2628
| test.cpp:59:9:59:11 | arr indirection [p] | test.cpp:59:13:59:13 | p indirection |
2729
| test.cpp:59:13:59:13 | p indirection | test.cpp:59:13:59:13 | p |
2830
| test.cpp:63:9:63:11 | arr indirection [p] | test.cpp:63:13:63:13 | p indirection |
2931
| test.cpp:63:13:63:13 | p indirection | test.cpp:63:13:63:13 | p |
3032
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] |
3133
| test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:98:18:98:27 | call to mk_array_p indirection [p] |
3234
| test.cpp:69:5:69:25 | ... = ... | test.cpp:69:10:69:10 | arr indirection [post update] [p] |
33-
| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] |
35+
| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:70:5:70:7 | arr indirection [p] |
3436
| test.cpp:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... |
37+
| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] |
3538
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:79:9:79:11 | arr indirection [p] |
3639
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:83:9:83:11 | arr indirection [p] |
3740
| test.cpp:79:9:79:11 | arr indirection [p] | test.cpp:79:14:79:14 | p indirection |
@@ -53,6 +56,7 @@ nodes
5356
| test.cpp:21:5:21:24 | ... = ... | semmle.label | ... = ... |
5457
| test.cpp:21:9:21:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
5558
| test.cpp:21:13:21:18 | call to malloc | semmle.label | call to malloc |
59+
| test.cpp:22:5:22:7 | arr indirection [p] | semmle.label | arr indirection [p] |
5660
| test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] |
5761
| test.cpp:31:9:31:11 | arr indirection [p] | semmle.label | arr indirection [p] |
5862
| test.cpp:31:13:31:13 | p | semmle.label | p |
@@ -71,6 +75,7 @@ nodes
7175
| test.cpp:55:5:55:24 | ... = ... | semmle.label | ... = ... |
7276
| test.cpp:55:9:55:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
7377
| test.cpp:55:13:55:18 | call to malloc | semmle.label | call to malloc |
78+
| test.cpp:56:5:56:7 | arr indirection [p] | semmle.label | arr indirection [p] |
7479
| test.cpp:59:9:59:11 | arr indirection [p] | semmle.label | arr indirection [p] |
7580
| test.cpp:59:13:59:13 | p | semmle.label | p |
7681
| test.cpp:59:13:59:13 | p indirection | semmle.label | p indirection |
@@ -81,6 +86,7 @@ nodes
8186
| test.cpp:69:5:69:25 | ... = ... | semmle.label | ... = ... |
8287
| test.cpp:69:10:69:10 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] |
8388
| test.cpp:69:14:69:19 | call to malloc | semmle.label | call to malloc |
89+
| test.cpp:70:5:70:7 | arr indirection [p] | semmle.label | arr indirection [p] |
8490
| test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] |
8591
| test.cpp:79:9:79:11 | arr indirection [p] | semmle.label | arr indirection [p] |
8692
| test.cpp:79:14:79:14 | p | semmle.label | p |

0 commit comments

Comments
 (0)