Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 031a48e

Browse files
authored
Merge pull request #296 from owen-mc/allocation-size-overflow-improve-sanitizers-easy
Add new sanitizer guard to Allocation size overflow query
2 parents b4550f2 + a669fa4 commit 031a48e

File tree

4 files changed

+71
-4
lines changed

4 files changed

+71
-4
lines changed

ql/src/Security/CWE-190/AllocationSizeOverflow.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ where
2121
cfg.hasFlowPath(source, sink) and
2222
cfg.isSink(sink.getNode(), allocsz)
2323
select sink, source, sink,
24-
"This operation, which is used in an $@, involves a potentially large $@ " + "and might overflow.",
24+
"This operation, which is used in an $@, involves a potentially large $@ and might overflow.",
2525
allocsz, "allocation", source, "value"

ql/src/semmle/go/security/AllocationSizeOverflowCustomizations.qll

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ module AllocationSizeOverflow {
7272
}
7373
}
7474

75+
/** A check of the allocation size, acting as a guard to prevent allocation-size overflow. */
76+
class AllocationSizeCheck extends DataFlow::BarrierGuard, DataFlow::RelationalComparisonNode {
77+
override predicate checks(Expr e, boolean branch) {
78+
exists(DataFlow::Node lesser | this.leq(branch, lesser, _, _) and not lesser.isConst() |
79+
globalValueNumber(DataFlow::exprNode(e)) = globalValueNumber(lesser)
80+
)
81+
}
82+
}
83+
7584
/**
7685
* An arithmetic operation that might overflow, and whose result is used to compute an
7786
* allocation size.
@@ -81,7 +90,8 @@ module AllocationSizeOverflow {
8190

8291
DefaultSink() {
8392
this instanceof OverflowProneOperand and
84-
localStep*(this, allocsz)
93+
localStep*(this, allocsz) and
94+
not exists(AllocationSizeCheck g | allocsz = g.getAGuardedNode())
8595
}
8696

8797
override DataFlow::Node getAllocationSize() { result = allocsz }
@@ -165,7 +175,7 @@ module AllocationSizeOverflow {
165175

166176
/**
167177
* Holds if the value of `pred` can flow into `succ` in one step, either through a call to `len`
168-
* or through an arithmetic operation.
178+
* or through an arithmetic operation (other than remainder).
169179
*/
170180
predicate additionalStep(DataFlow::Node pred, DataFlow::Node succ) {
171181
exists(DataFlow::CallNode c |
@@ -174,7 +184,8 @@ module AllocationSizeOverflow {
174184
succ = c
175185
)
176186
or
177-
succ.asExpr().(ArithmeticExpr).getAnOperand() = pred.asExpr()
187+
succ.asExpr().(ArithmeticExpr).getAnOperand() = pred.asExpr() and
188+
not succ.asExpr() instanceof RemExpr
178189
}
179190

180191
/**

ql/test/query-tests/Security/CWE-190/AllocationSizeOverflow.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ edges
22
| AllocationSizeOverflow.go:6:2:6:33 | ... := ...[0] : slice type | AllocationSizeOverflow.go:10:10:10:22 | call to len |
33
| tst2.go:9:2:9:37 | ... := ...[0] : slice type | tst2.go:10:22:10:30 | call to len |
44
| tst2.go:14:2:14:29 | ... := ...[0] : slice type | tst2.go:15:22:15:30 | call to len |
5+
| tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:7:22:7:34 | call to len |
6+
| tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:24:16:24:28 | call to len |
7+
| tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:32:16:32:28 | call to len |
58
| tst.go:14:2:14:30 | ... = ...[0] : slice type | tst.go:15:22:15:34 | call to len |
69
| tst.go:20:2:20:31 | ... = ...[0] : slice type | tst.go:21:22:21:34 | call to len |
710
| tst.go:26:2:26:31 | ... = ...[0] : slice type | tst.go:27:26:27:38 | call to len |
@@ -13,6 +16,10 @@ nodes
1316
| tst2.go:10:22:10:30 | call to len | semmle.label | call to len |
1417
| tst2.go:14:2:14:29 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
1518
| tst2.go:15:22:15:30 | call to len | semmle.label | call to len |
19+
| tst3.go:6:2:6:31 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
20+
| tst3.go:7:22:7:34 | call to len | semmle.label | call to len |
21+
| tst3.go:24:16:24:28 | call to len | semmle.label | call to len |
22+
| tst3.go:32:16:32:28 | call to len | semmle.label | call to len |
1623
| tst.go:14:2:14:30 | ... = ...[0] : slice type | semmle.label | ... = ...[0] : slice type |
1724
| tst.go:15:22:15:34 | call to len | semmle.label | call to len |
1825
| tst.go:20:2:20:31 | ... = ...[0] : slice type | semmle.label | ... = ...[0] : slice type |
@@ -25,6 +32,9 @@ nodes
2532
| AllocationSizeOverflow.go:10:10:10:22 | call to len | AllocationSizeOverflow.go:6:2:6:33 | ... := ...[0] : slice type | AllocationSizeOverflow.go:10:10:10:22 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | AllocationSizeOverflow.go:11:25:11:28 | size | allocation | AllocationSizeOverflow.go:6:2:6:33 | ... := ...[0] : slice type | value |
2633
| tst2.go:10:22:10:30 | call to len | tst2.go:9:2:9:37 | ... := ...[0] : slice type | tst2.go:10:22:10:30 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst2.go:10:22:10:32 | ...+... | allocation | tst2.go:9:2:9:37 | ... := ...[0] : slice type | value |
2734
| tst2.go:15:22:15:30 | call to len | tst2.go:14:2:14:29 | ... := ...[0] : slice type | tst2.go:15:22:15:30 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst2.go:15:22:15:32 | ...+... | allocation | tst2.go:14:2:14:29 | ... := ...[0] : slice type | value |
35+
| tst3.go:7:22:7:34 | call to len | tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:7:22:7:34 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst3.go:7:22:7:36 | ...+... | allocation | tst3.go:6:2:6:31 | ... := ...[0] : slice type | value |
36+
| tst3.go:24:16:24:28 | call to len | tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:24:16:24:28 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst3.go:27:24:27:32 | newlength | allocation | tst3.go:6:2:6:31 | ... := ...[0] : slice type | value |
37+
| tst3.go:32:16:32:28 | call to len | tst3.go:6:2:6:31 | ... := ...[0] : slice type | tst3.go:32:16:32:28 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst3.go:36:23:36:31 | newlength | allocation | tst3.go:6:2:6:31 | ... := ...[0] : slice type | value |
2838
| tst.go:15:22:15:34 | call to len | tst.go:14:2:14:30 | ... = ...[0] : slice type | tst.go:15:22:15:34 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst.go:15:22:15:36 | ...+... | allocation | tst.go:14:2:14:30 | ... = ...[0] : slice type | value |
2939
| tst.go:21:22:21:34 | call to len | tst.go:20:2:20:31 | ... = ...[0] : slice type | tst.go:21:22:21:34 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst.go:21:22:21:36 | ...+... | allocation | tst.go:20:2:20:31 | ... = ...[0] : slice type | value |
3040
| tst.go:27:26:27:38 | call to len | tst.go:26:2:26:31 | ... = ...[0] : slice type | tst.go:27:26:27:38 | call to len | This operation, which is used in an $@, involves a potentially large $@ and might overflow. | tst.go:27:26:27:40 | ...+... | allocation | tst.go:26:2:26:31 | ... = ...[0] : slice type | value |
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
import "encoding/json"
4+
5+
func testSanitizers(s string) {
6+
jsonData, _ := json.Marshal(s)
7+
ignore(make([]byte, len(jsonData)+1)) // NOT OK: data might be big
8+
9+
ignore(make([]byte, int64(len(jsonData))+1)) // OK: sanitized by widening to 64 bits
10+
11+
if len(jsonData) < 1000 {
12+
ignore(make([]byte, len(jsonData)+1)) // OK: there is an upper bound check on len(jsonData)
13+
}
14+
15+
{
16+
newlength := len(jsonData) + 2 // OK: there is an upper bound check which dominates `make`
17+
_ := newlength - 1
18+
if newlength < 1000 {
19+
ignore(make([]byte, newlength))
20+
}
21+
}
22+
23+
{
24+
newlength := len(jsonData) + 3 // NOT OK: newlength is changed after the upper bound check (even though it's made smaller)
25+
if newlength < 1000 {
26+
newlength = newlength - 1
27+
ignore(make([]byte, newlength))
28+
}
29+
}
30+
31+
{
32+
newlength := len(jsonData) + 4 // NOT OK: there is an upper bound check but it doesn't dominate `make`
33+
if newlength < 1000 {
34+
_ := newlength + 2
35+
}
36+
ignore(make([]byte, newlength))
37+
}
38+
39+
{
40+
newlength := len(jsonData) + 5 // OK: there is an upper bound check which dominates `make`
41+
if newlength > 1000 {
42+
return
43+
}
44+
ignore(make([]byte, newlength))
45+
}
46+
}

0 commit comments

Comments
 (0)