-
Notifications
You must be signed in to change notification settings - Fork 61
/
Copy pathDoNotAttemptToModifyStringLiterals.ql
154 lines (141 loc) · 5.2 KB
/
DoNotAttemptToModifyStringLiterals.ql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/**
* @id c/cert/do-not-attempt-to-modify-string-literals
* @name STR30-C: Do not attempt to modify string literals
* @description Modifying a string literal can produce unexpected effects.
* @kind problem
* @precision very-high
* @problem.severity error
* @tags external/cert/id/str30-c
* correctness
* security
* external/cert/obligation/rule
*/
import cpp
import codingstandards.c.cert
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.dataflow.new.DataFlow
/**
* Class that includes into `BufferWrite` functions that will modify their
* first argument. This is an extension of `BufferWrite` which covers the case
* of opaque writes via library functions.
*/
class ModifiesFirstArgFunction extends BufferWrite, FunctionCall {
Expr modifiedExpr;
ModifiesFirstArgFunction() {
getTarget().getName() = ["mkstemp", "memset", "memcpy", "memmove"] and
getArgument(0) = modifiedExpr
}
override Type getBufferType() { none() }
override Expr getDest() { result = modifiedExpr }
}
/**
* Models a dataflow wherein a source is either a implicit or explicit string
* literal that is assigned to a non modifiable type or wherein the string
* literal arises as a argument to a function that may modify its argument.
*/
module ImplicitOrExplicitStringLiteralModifiedConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
// usage through variables
exists(Variable v |
v.getAnAssignedValue() = node.asExpr() and
(
node.asExpr() instanceof ImplicitStringLiteral or
node.asExpr() instanceof StringLiteralOrConstChar
) and
v.getType().getUnderlyingType() instanceof CharPointerType
)
or
// direct usage of string literals as function parameters
exists(BufferWrite bw |
bw.getDest() = node.asExpr() and
(
node.asExpr() instanceof ImplicitStringLiteral or
node.asExpr() instanceof StringLiteralOrConstChar
)
)
}
predicate isSink(DataFlow::Node node) {
// it's either a buffer write of some kind that we
// know about
exists(BufferWrite bw | bw.getDest() = node.asExpr())
or
// or it is a direct assignment of some kind - including reassignment of the pointer
exists(AssignExpr aexp | aexp.getLValue().(ArrayExpr).getArrayBase() = node.asExpr())
or
exists(AssignExpr aexp | aexp.getLValue().(PointerDereferenceExpr).getOperand() = node.asExpr())
}
}
module ImplicitOrExplicitStringLiteralModifiedFlow =
DataFlow::Global<ImplicitOrExplicitStringLiteralModifiedConfig>;
class MaybeReturnsStringLiteralFunctionCall extends FunctionCall {
MaybeReturnsStringLiteralFunctionCall() {
getTarget().getName() in [
"strpbrk", "strchr", "strrchr", "strstr", "wcspbrk", "wcschr", "wcsrchr", "wcsstr",
"memchr", "wmemchr"
]
}
}
class ImplicitStringLiteral extends Expr {
ImplicitStringLiteral() {
exists(MaybeReturnsStringLiteralFunctionCall fc, Variable e |
e.getAnAssignedValue() = fc and
this = fc and
// additionally, we require that the first argument is either an explicit
// or implicit string literal
(
// directly a string literal
fc.getArgument(0) instanceof StringLiteralOrConstChar
or
// a string literal flows into it
exists(StringLiteralOrConstChar sl |
DataFlow::localFlow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0)))
)
or
// or a base flows into it
exists(ImplicitStringLiteralBase base |
DataFlow::localFlow(DataFlow::exprNode(base), DataFlow::exprNode(fc.getArgument(0)))
)
)
)
}
}
class StringLiteralOrConstChar extends Expr {
StringLiteralOrConstChar() {
this instanceof StringLiteral
or
getUnspecifiedType() instanceof CharPointerType and
getType().(PointerType).getBaseType().isConst()
}
}
/**
* Since it is possible to produce an implicit literal by either
* an explicit literal being passed to one of these functions this
* class exists to establish the "base" type, that is an explicit
* string literal passed or flowing into the first argument. The other
* Implicit string literal class will then check to see if it is inductively
* an implicit string literal.
*/
class ImplicitStringLiteralBase extends Expr {
ImplicitStringLiteralBase() {
exists(MaybeReturnsStringLiteralFunctionCall fc, Variable e |
e.getAnAssignedValue() = fc and
this = fc and
// it either directly gets a string literal or one via flow
(
fc.getArgument(0) instanceof StringLiteralOrConstChar or
exists(StringLiteralOrConstChar sl |
DataFlow::localFlow(DataFlow::exprNode(sl), DataFlow::exprNode(fc.getArgument(0)))
)
)
)
}
}
from Expr literal, Expr literalWrite
where
not isExcluded(literal, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and
not isExcluded(literalWrite, Strings1Package::doNotAttemptToModifyStringLiteralsQuery()) and
ImplicitOrExplicitStringLiteralModifiedFlow::flow(DataFlow::exprNode(literal),
DataFlow::exprNode(literalWrite))
select literalWrite,
"This operation may write to a string that may be a string literal that was $@.", literal,
"created here"