|
5 | 5 | * @kind problem |
6 | 6 | * @id cpp/overflow-destination |
7 | 7 | * @problem.severity warning |
| 8 | + * @precision low |
8 | 9 | * @tags reliability |
9 | 10 | * security |
10 | 11 | * external/cwe/cwe-119 |
11 | 12 | * external/cwe/cwe-131 |
12 | 13 | */ |
13 | 14 | import cpp |
14 | | -import semmle.code.cpp.pointsto.PointsTo |
| 15 | +import semmle.code.cpp.security.TaintTracking |
15 | 16 |
|
16 | | -predicate sourceSized(FunctionCall fc) |
| 17 | +/** |
| 18 | + * Holds if `fc` is a call to a copy operation where the size argument contains |
| 19 | + * a reference to the source argument. For example: |
| 20 | + * ``` |
| 21 | + * memcpy(dest, src, sizeof(src)); |
| 22 | + * ``` |
| 23 | + */ |
| 24 | +predicate sourceSized(FunctionCall fc, Expr src) |
17 | 25 | { |
18 | 26 | exists(string name | |
19 | 27 | (name = "strncpy" or name = "strncat" or name = "memcpy" or name = "memmove") and |
20 | 28 | fc.getTarget().hasQualifiedName(name)) |
21 | 29 | and |
22 | | - exists(Expr dest, Expr src, Expr size, Variable v | |
| 30 | + exists(Expr dest, Expr size, Variable v | |
23 | 31 | fc.getArgument(0) = dest and fc.getArgument(1) = src and fc.getArgument(2) = size and |
24 | 32 | src = v.getAnAccess() and size.getAChild+() = v.getAnAccess() and |
| 33 | + |
| 34 | + // exception: `dest` is also referenced in the size argument |
25 | 35 | not exists(Variable other | |
26 | 36 | dest = other.getAnAccess() and size.getAChild+() = other.getAnAccess()) |
27 | 37 | and |
| 38 | + |
| 39 | + // exception: `src` and `dest` are both arrays of the same type and size |
28 | 40 | not exists(ArrayType srctype, ArrayType desttype | |
29 | 41 | dest.getType().getUnderlyingType() = desttype and |
30 | 42 | src.getType().getUnderlyingType() = srctype and |
31 | 43 | desttype.getBaseType().getUnderlyingType() = srctype.getBaseType().getUnderlyingType() and |
32 | 44 | desttype.getArraySize() = srctype.getArraySize())) |
33 | 45 | } |
34 | 46 |
|
35 | | -class VulnerableArgument extends PointsToExpr |
36 | | -{ |
37 | | - VulnerableArgument() { sourceSized(this.getParent()) } |
38 | | - override predicate interesting() { sourceSized(this.getParent()) } |
39 | | -} |
40 | | - |
41 | | -predicate taintingFunction(Function f, int buf) |
42 | | -{ |
43 | | - (f.hasQualifiedName("read") and buf = 1) or |
44 | | - (f.hasQualifiedName("fgets") and buf = 0) or |
45 | | - (f.hasQualifiedName("fread") and buf = 0) |
46 | | -} |
47 | | - |
48 | | -// Taint `argv[i]`, for all i, but also `*argv`, etc. |
49 | | -predicate commandLineArg(Expr e) |
50 | | -{ |
51 | | - exists(Function f, Parameter argv, VariableAccess access | |
52 | | - f.hasQualifiedName("main") and f.getParameter(1) = argv and |
53 | | - argv.getAnAccess() = access and access.isRValue() and |
54 | | - pointer(access, e)) |
55 | | -} |
56 | | - |
57 | | -predicate tainted(Expr e) |
58 | | -{ |
59 | | - exists(FunctionCall fc, int arg | |
60 | | - taintingFunction(fc.getTarget(), arg) and |
61 | | - e = fc.getArgument(arg)) |
62 | | - or |
63 | | - e.(FunctionCall).getTarget().hasQualifiedName("getenv") |
64 | | - or |
65 | | - commandLineArg(e) |
66 | | -} |
67 | | - |
68 | | -class TaintedArgument extends PointsToExpr |
69 | | -{ |
70 | | - TaintedArgument() { tainted(this) } |
71 | | - override predicate interesting() { tainted(this) } |
72 | | -} |
73 | | - |
74 | | -from FunctionCall fc, VulnerableArgument vuln, TaintedArgument tainted |
75 | | -where sourceSized(fc) |
76 | | - and fc.getArgument(1) = vuln |
77 | | - and vuln.pointsTo() = tainted.pointsTo() |
78 | | - and vuln.confidence() > 0.01 |
| 47 | +from FunctionCall fc, Expr vuln, Expr taintSource |
| 48 | +where sourceSized(fc, vuln) |
| 49 | + and tainted(taintSource, vuln) |
79 | 50 | select fc, "To avoid overflow, this operation should be bounded by destination-buffer size, not source-buffer size." |
0 commit comments