|
19 | 19 | import cpp |
20 | 20 | import codingstandards.c.cert |
21 | 21 | import semmle.code.cpp.security.BufferWrite |
22 | | -import semmle.code.cpp.dataflow.DataFlow |
23 | | - |
24 | | -/** |
25 | | - * Class that includes into `BufferWrite` functions that will modify their |
26 | | - * first argument. This is an extension of `BufferWrite` which covers the case |
27 | | - * of opaque writes via library functions. |
28 | | - */ |
29 | | -class ModifiesFirstArgFunction extends BufferWrite, FunctionCall { |
30 | | - Expr modifiedExpr; |
| 22 | +import semmle.code.cpp.dataflow.new.DataFlow |
31 | 23 |
|
| 24 | +/** A modeled buffer write through the first argument of a library call. */ |
| 25 | +private class ModifiesFirstArgFunction extends BufferWrite, FunctionCall { |
32 | 26 | ModifiesFirstArgFunction() { |
33 | | - getTarget().getName() = ["mkstemp", "memset", "memcpy", "memmove"] and |
34 | | - getArgument(0) = modifiedExpr |
| 27 | + getTarget().getName() = ["mkstemp", "memset", "memcpy", "memmove"] |
35 | 28 | } |
36 | 29 |
|
37 | 30 | override Type getBufferType() { none() } |
38 | 31 |
|
39 | | - override Expr getDest() { result = modifiedExpr } |
| 32 | + override Expr getDest() { result = getArgument(0) } |
40 | 33 | } |
41 | 34 |
|
42 | | -/** |
43 | | - * Models a dataflow wherein a source is either a implicit or explicit string |
44 | | - * literal that is assigned to a non modifiable type or wherein the string |
45 | | - * literal arises as a argument to a function that may modify its argument. |
46 | | - */ |
47 | | -module ImplicitOrExplicitStringLiteralModifiedConfig implements DataFlow::ConfigSig { |
| 35 | +/** Provides dataflow from assigned string literals to writes. */ |
| 36 | +private module StringLiteralConfig implements DataFlow::ConfigSig { |
48 | 37 | predicate isSource(DataFlow::Node node) { |
49 | | - // usage through variables |
50 | 38 | exists(Variable v | |
51 | 39 | v.getAnAssignedValue() = node.asExpr() and |
52 | | - ( |
53 | | - node.asExpr() instanceof ImplicitStringLiteral or |
54 | | - node.asExpr() instanceof StringLiteralOrConstChar |
55 | | - ) and |
| 40 | + mayBeStringLiteral(node.asExpr()) and |
56 | 41 | v.getType().getUnderlyingType() instanceof CharPointerType |
57 | 42 | ) |
58 | | - or |
59 | | - // direct usage of string literals as function parameters |
60 | | - exists(BufferWrite bw | |
61 | | - bw.getDest() = node.asExpr() and |
62 | | - ( |
63 | | - node.asExpr() instanceof ImplicitStringLiteral or |
64 | | - node.asExpr() instanceof StringLiteralOrConstChar |
65 | | - ) |
66 | | - ) |
67 | 43 | } |
68 | 44 |
|
69 | 45 | predicate isSink(DataFlow::Node node) { |
70 | | - // it's either a buffer write of some kind that we |
71 | | - // know about |
72 | | - exists(BufferWrite bw | bw.getDest() = node.asExpr()) |
| 46 | + node.asExpr() = any(BufferWrite bw).getDest() |
73 | 47 | or |
74 | | - // or it is a direct assignment of some kind - including reassignment of the pointer |
75 | | - exists(AssignExpr aexp | aexp.getLValue().(ArrayExpr).getArrayBase() = node.asExpr()) |
| 48 | + node.asExpr() = any(AssignExpr a).getLValue().(ArrayExpr).getArrayBase() |
76 | 49 | or |
77 | | - exists(AssignExpr aexp | aexp.getLValue().(PointerDereferenceExpr).getOperand() = node.asExpr()) |
| 50 | + node.asExpr() = any(AssignExpr a).getLValue().(PointerDereferenceExpr).getOperand() |
78 | 51 | } |
79 | 52 | } |
80 | 53 |
|
81 | | -module ImplicitOrExplicitStringLiteralModifiedFlow = |
82 | | - DataFlow::Global<ImplicitOrExplicitStringLiteralModifiedConfig>; |
| 54 | +/** Provides dataflow from possible string literals to writes. */ |
| 55 | +private module StringLiteralFlow { |
| 56 | + private module Global = DataFlow::Global<StringLiteralConfig>; |
83 | 57 |
|
84 | | -class MaybeReturnsStringLiteralFunctionCall extends FunctionCall { |
85 | | - MaybeReturnsStringLiteralFunctionCall() { |
86 | | - getTarget().getName() in [ |
87 | | - "strpbrk", "strchr", "strrchr", "strstr", "wcspbrk", "wcschr", "wcsrchr", "wcsstr", |
88 | | - "memchr", "wmemchr" |
89 | | - ] |
| 58 | + /** Holds if `source` may point to a string literal that is written at `sink`. */ |
| 59 | + predicate flow(Expr source, Expr sink) { |
| 60 | + // Report the pointer operand rather than a dereference represented by the same dataflow node. |
| 61 | + not sink instanceof PointerDereferenceExpr and |
| 62 | + ( |
| 63 | + Global::flow(DataFlow::exprNode(source), DataFlow::exprNode(sink)) |
| 64 | + or |
| 65 | + source = sink and |
| 66 | + mayBeStringLiteral(sink) and |
| 67 | + sink = any(BufferWrite bw).getDest() |
| 68 | + ) |
90 | 69 | } |
91 | 70 | } |
92 | 71 |
|
93 | | -class ImplicitStringLiteral extends Expr { |
| 72 | +/** A call that may return a pointer into a possible string literal. */ |
| 73 | +private class ImplicitStringLiteral extends FunctionCall { |
94 | 74 | ImplicitStringLiteral() { |
95 | | - exists(MaybeReturnsStringLiteralFunctionCall fc, Variable e | |
96 | | - e.getAnAssignedValue() = fc and |
97 | | - this = fc and |
98 | | - // additionally, we require that the first argument is either an explicit |
99 | | - // or implicit string literal |
100 | | - ( |
101 | | - // directly a string literal |
102 | | - fc.getArgument(0) instanceof StringLiteralOrConstChar |
103 | | - or |
104 | | - // a string literal flows into it |
105 | | - exists(StringLiteralOrConstChar sl | |
106 | | - DataFlow::localFlow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0))) |
107 | | - ) |
108 | | - or |
109 | | - // or a base flows into it |
110 | | - exists(ImplicitStringLiteralBase base | |
111 | | - DataFlow::localFlow(DataFlow::exprNode(base), DataFlow::exprNode(fc.getArgument(0))) |
112 | | - ) |
113 | | - ) |
| 75 | + getTarget().getName() in [ |
| 76 | + "strpbrk", "strchr", "strrchr", "strstr", "wcspbrk", "wcschr", "wcsrchr", "wcsstr", |
| 77 | + "memchr", "wmemchr" |
| 78 | + ] and |
| 79 | + exists(Variable v | v.getAnAssignedValue() = this) and |
| 80 | + exists(Expr source | |
| 81 | + mayBeStringLiteral(source) and DataFlow::localExprFlow(source, getArgument(0)) |
114 | 82 | ) |
115 | 83 | } |
116 | 84 | } |
117 | 85 |
|
118 | | -class StringLiteralOrConstChar extends Expr { |
119 | | - StringLiteralOrConstChar() { |
120 | | - this instanceof StringLiteral |
121 | | - or |
122 | | - getUnspecifiedType() instanceof CharPointerType and |
123 | | - getType().(PointerType).getBaseType().isConst() |
124 | | - } |
125 | | -} |
126 | | - |
127 | | -/** |
128 | | - * Since it is possible to produce an implicit literal by either |
129 | | - * an explicit literal being passed to one of these functions this |
130 | | - * class exists to establish the "base" type, that is an explicit |
131 | | - * string literal passed or flowing into the first argument. The other |
132 | | - * Implicit string literal class will then check to see if it is inductively |
133 | | - * an implicit string literal. |
134 | | - */ |
135 | | -class ImplicitStringLiteralBase extends Expr { |
136 | | - ImplicitStringLiteralBase() { |
137 | | - exists(MaybeReturnsStringLiteralFunctionCall fc, Variable e | |
138 | | - e.getAnAssignedValue() = fc and |
139 | | - this = fc and |
140 | | - // it either directly gets a string literal or one via flow |
141 | | - ( |
142 | | - fc.getArgument(0) instanceof StringLiteralOrConstChar or |
143 | | - exists(StringLiteralOrConstChar sl | |
144 | | - DataFlow::localFlow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0))) |
145 | | - ) |
146 | | - ) |
147 | | - ) |
148 | | - } |
| 86 | +/** Holds if `e` may point to a string literal. */ |
| 87 | +private predicate mayBeStringLiteral(Expr e) { |
| 88 | + e instanceof StringLiteral |
| 89 | + or |
| 90 | + e.getUnspecifiedType() instanceof CharPointerType and |
| 91 | + e.getType().(PointerType).getBaseType().isConst() |
| 92 | + or |
| 93 | + e instanceof ImplicitStringLiteral |
149 | 94 | } |
150 | 95 |
|
151 | 96 | from Expr literal, Expr literalWrite |
152 | 97 | where |
153 | 98 | not isExcluded(literal, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and |
154 | 99 | not isExcluded(literalWrite, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and |
155 | | - ImplicitOrExplicitStringLiteralModifiedFlow::flow(DataFlow::exprNode(literal), |
156 | | - DataFlow::exprNode(literalWrite)) |
| 100 | + StringLiteralFlow::flow(literal, literalWrite) |
157 | 101 | select literalWrite, |
158 | 102 | "This operation may write to a string that may be a string literal that was $@.", literal, |
159 | 103 | "created here" |
0 commit comments