Skip to content

Commit bd0fd70

Browse files
committed
docstrings for implicit conversions, final version
1 parent 9ce6665 commit bd0fd70

File tree

2 files changed

+59
-55
lines changed

2 files changed

+59
-55
lines changed

cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.qhelp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,29 @@
66
<p>
77
Integer variables may be implicitly casted to a type of different size and signedness.
88
If the variable is casted to a type of smaller bit-size or different signedness without a proper bound checking,
9-
then the casting may silently truncate the variable's value or make it semantically meaningless.
9+
then the casting may silently change the variable's value or make it semantically meaningless.
1010

11-
This query finds implicit casts that cannot be proven to be safe.
11+
Since implicit casts are introduced by the compiler, developers may be not aware of them and the compiled code
12+
may behave incorrectly aka may have bugs.
13+
14+
This query finds implicit casts that cannot be proven to be safe.
15+
Safe means that the input value is known to fit into destination type aka the value won't change.
1216
</p>
1317

1418
</overview>
1519
<recommendation>
16-
<p>Either change variables types to avoid implicit conversions or verify that converting highlighted variables is always safe.</p>
20+
<p>
21+
Either adjust types of problematic variables to avoid implicit conversions,
22+
make the code validate that converting the variables is safe,
23+
or add explicit conversions that would make the compiler avoid introducing implicit ones.
24+
</p>
1725

1826
</recommendation>
1927
<example>
2028
<sample src="UnsafeImplicitConversions.cpp" />
2129

22-
<p>In this example, the call to <code>malloc_wrapper</code> may silently truncate <code>large</code> variable, and so the allocated buffer will be of smaller size than the <code>test</code> function expects.</p>
30+
<p>In this example, the call to <code>malloc_wrapper</code> may silently truncate <code>large</code> variable
31+
so that the allocated buffer will be of smaller size than the <code>test</code> function expects.</p>
2332
</example>
2433

2534
</qhelp>

cpp/src/security/UnsafeImplicitConversions/UnsafeImplicitConversions.ql

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @name Find all problematic implicit casts
3-
* @description Find all implicit casts that may be problematic. That is, may result in unexpected truncation, reinterpretation or widening of values.
3+
* @description Find all implicit casts that may be problematic. That is, casts that may result in unexpected truncation, reinterpretation or widening of values.
44
* @kind problem
55
* @id tob/cpp/unsafe-implicit-conversions
66
* @tags security
@@ -17,22 +17,10 @@ private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis
1717
private import semmle.code.cpp.ir.IR
1818
private import semmle.code.cpp.ir.ValueNumbering
1919

20-
private class BufLenFunc extends SimpleRangeAnalysisExpr, FunctionCall {
21-
BufLenFunc() {
22-
this.getTarget()
23-
.getName()
24-
.matches([
25-
"buf_len", "buf_reverse_capacity", "buf_forward_capacity", "buf_forward_capacity_total"
26-
])
27-
}
28-
29-
override float getLowerBounds() { result = 0 }
30-
31-
override float getUpperBounds() { result = typeUpperBound(this.getExpectedReturnType()) }
32-
33-
override predicate dependsOnChild(Expr child) { none() }
34-
}
35-
20+
/**
21+
* Models standard I/O functions that return a length value bounded by their size argument
22+
* with possible -1 error return value
23+
*/
3624
private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall {
3725
LenApproxFunc() {
3826
this.getTarget().hasName(["recvfrom", "recv", "sendto", "send", "read", "write"])
@@ -45,6 +33,11 @@ private class LenApproxFunc extends SimpleRangeAnalysisExpr, FunctionCall {
4533
override predicate dependsOnChild(Expr child) { child = this.getArgument(2) }
4634
}
4735

36+
/*
37+
* Uncomment the class below to silent findings that require large strings
38+
* to be passed to strlen to be exploitable.
39+
*/
40+
/*
4841
private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall {
4942
StrlenFunAssumption() { this.getTarget().hasName("strlen") }
5043
@@ -54,28 +47,13 @@ private class StrlenFunAssumption extends SimpleRangeAnalysisExpr, FunctionCall
5447
5548
override predicate dependsOnChild(Expr child) { none() }
5649
}
50+
*/
5751

58-
private class OpenSSLFunc extends SimpleRangeAnalysisExpr, FunctionCall {
59-
OpenSSLFunc() {
60-
this.getTarget()
61-
.getName()
62-
.matches([
63-
"EVP_CIPHER_get_block_size", "cipher_ctx_block_size", "EVP_CIPHER_CTX_get_block_size",
64-
"EVP_CIPHER_block_size", "HMAC_size", "hmac_ctx_size", "EVP_MAC_CTX_get_mac_size",
65-
"EVP_CIPHER_CTX_mode", "EVP_CIPHER_CTX_get_mode", "EVP_CIPHER_iv_length",
66-
"cipher_ctx_iv_length", "EVP_CIPHER_key_length", "EVP_MD_size", "EVP_MD_get_size",
67-
"cipher_kt_iv_size", "cipher_kt_block_size", "EVP_PKEY_get_size", "EVP_PKEY_get_bits",
68-
"EVP_PKEY_get_security_bits"
69-
])
70-
}
71-
72-
override float getLowerBounds() { result = 0 }
73-
74-
override float getUpperBounds() { result = 32768 }
75-
76-
override predicate dependsOnChild(Expr child) { none() }
77-
}
78-
52+
/**
53+
* Determines if a function's address is taken in the codebase.
54+
* This indicates that the function may be called while
55+
* the call is not in the FunctionCall class.
56+
*/
7957
predicate addressIsTaken(Function f) {
8058
exists(FunctionCall call | call.getAnArgument().getFullyConverted() = f.getAnAccess())
8159
or
@@ -96,6 +74,9 @@ predicate addressIsTaken(Function f) {
9674
exists(ReturnStmt ret | ret.getExpr().getFullyConverted() = f.getAnAccess())
9775
}
9876

77+
/**
78+
* Propagates argument range information from function calls to function parameters
79+
*/
9980
class ConstrainArgs extends SimpleRangeAnalysisDefinition {
10081
private Function func;
10182
private Parameter param;
@@ -128,6 +109,9 @@ class ConstrainArgs extends SimpleRangeAnalysisDefinition {
128109
}
129110
}
130111

112+
/**
113+
* Helper to extract left and right operands from bitwise OR operations
114+
*/
131115
predicate getLeftRightOrOperands(Expr orExpr, Expr l, Expr r) {
132116
l = orExpr.(BitwiseOrExpr).getLeftOperand() and
133117
r = orExpr.(BitwiseOrExpr).getRightOperand()
@@ -136,6 +120,9 @@ predicate getLeftRightOrOperands(Expr orExpr, Expr l, Expr r) {
136120
r = orExpr.(AssignOrExpr).getRValue()
137121
}
138122

123+
/**
124+
* Provides range analysis for bitwise OR operations with non-negative constants
125+
*/
139126
private class ConstantBitwiseOrExprRange extends SimpleRangeAnalysisExpr {
140127
ConstantBitwiseOrExprRange() {
141128
exists(Expr l, Expr r | getLeftRightOrOperands(this, l, r) |
@@ -185,6 +172,10 @@ private class ConstantBitwiseOrExprRange extends SimpleRangeAnalysisExpr {
185172
}
186173
}
187174

175+
/**
176+
* Checks if an expression has a safe lower bound for conversion to the given type
177+
* using both SimpleRangeAnalysis and IR-based RangeAnalysis
178+
*/
188179
predicate safeLowerBound(Expr cast, IntegralType toType) {
189180
exists(float lowerB |
190181
lowerB = lowerBound(cast) and
@@ -200,6 +191,10 @@ predicate safeLowerBound(Expr cast, IntegralType toType) {
200191
)
201192
}
202193

194+
/**
195+
* Checks if an expression has a safe upper bound for conversion to the given type
196+
* using both SimpleRangeAnalysis and IR-based RangeAnalysis
197+
*/
203198
predicate safeUpperBound(Expr cast, IntegralType toType) {
204199
exists(float upperB |
205200
upperB = upperBound(cast) and
@@ -215,6 +210,9 @@ predicate safeUpperBound(Expr cast, IntegralType toType) {
215210
)
216211
}
217212

213+
/**
214+
* Checks if an expression has both safe lower and upper bounds for conversion to the given type
215+
*/
218216
predicate safeBounds(Expr cast, IntegralType toType) {
219217
safeLowerBound(cast, toType) and safeUpperBound(cast, toType)
220218
}
@@ -223,12 +221,16 @@ from
223221
IntegralConversion cast, IntegralType fromType, IntegralType toType, Expr castExpr,
224222
string problemType
225223
where
224+
cast.getExpr() = castExpr and
225+
// only implicit conversions
226226
cast.isImplicit() and
227-
fromType = cast.getExpr().getExplicitlyConverted().getUnspecifiedType() and
227+
// the cast expression has attached all explicit and implicit casts; we skip all conversions up to the last explicit casts
228+
fromType = castExpr.getExplicitlyConverted().getUnspecifiedType() and
228229
toType = cast.getUnspecifiedType() and
230+
// skip same type casts and casts to bools
229231
fromType != toType and
230232
not toType instanceof BoolType and
231-
cast.getExpr() = castExpr and
233+
// limit findings to only possibly problematic cases
232234
(
233235
// truncation
234236
problemType = "truncation" and
@@ -257,9 +259,9 @@ where
257259
exists(ComplementExpr complement | complement.getOperand().getConversion*() = cast)
258260
)
259261
) and
262+
// skip conversions in some arithmetic operations
260263
not (
261-
// skip conversions in arithmetic operations
262-
fromType.getSize() <= toType.getSize() and // should always hold
264+
fromType.getSize() <= toType.getSize() and
263265
exists(BinaryArithmeticOperation arithmetic |
264266
(
265267
arithmetic instanceof AddExpr or
@@ -269,8 +271,8 @@ where
269271
arithmetic.getAnOperand().getConversion*() = cast
270272
)
271273
) and
274+
// skip some conversions in some equality operations
272275
not (
273-
// skip some conversions in equality operations
274276
fromType.getSize() <= toType.getSize() and
275277
fromType.isSigned() and // should always hold
276278
exists(EqualityOperation eq, Expr castHandSide, Expr otherHandSide |
@@ -290,12 +292,5 @@ where
290292
cast.getEnclosingFunction().getName() = "main"
291293
or
292294
addressIsTaken(cast.getEnclosingFunction())
293-
) and
294-
// skip casts in call to msg
295-
not exists(MacroInvocation msgCall |
296-
msgCall.getMacro().hasName("msg") and
297-
msgCall.getStmt().getAChild*() = cast.getEnclosingStmt()
298-
) and
299-
// not interesting file
300-
not cast.getLocation().getFile().getBaseName().matches("options.c")
295+
)
301296
select cast, "Implicit cast from " + fromType + " to " + toType + " (" + problemType + ")"

0 commit comments

Comments
 (0)