Skip to content

Commit 9822ec6

Browse files
authored
Merge pull request #263 from github/lcartey/integer-overflow
Implement `IntegerOverflow` package
2 parents d9dfdca + a2c8fe3 commit 9822ec6

File tree

50 files changed

+3020
-99
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3020
-99
lines changed

c/cert/src/rules/INT30-C/UnsignedIntegerOperationsWrapAround.md

Lines changed: 249 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @id c/cert/unsigned-integer-operations-wrap-around
3+
* @name INT30-C: Ensure that unsigned integer operations do not wrap
4+
* @description Unsigned integer expressions do not strictly overflow, but instead wrap around in a
5+
* modular way. If the size of the type is not sufficient, this can happen
6+
* unexpectedly.
7+
* @kind problem
8+
* @precision medium
9+
* @problem.severity error
10+
* @tags external/cert/id/int30-c
11+
* correctness
12+
* security
13+
* external/cert/obligation/rule
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.cert
18+
import codingstandards.cpp.Overflow
19+
import semmle.code.cpp.controlflow.Guards
20+
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
21+
22+
from InterestingOverflowingOperation op
23+
where
24+
not isExcluded(op, IntegerOverflowPackage::unsignedIntegerOperationsWrapAroundQuery()) and
25+
op.getType().getUnderlyingType().(IntegralType).isUnsigned() and
26+
// Not within a guard condition
27+
not exists(GuardCondition gc | gc.getAChild*() = op) and
28+
// Not guarded by a check, where the check is not an invalid overflow check
29+
not op.hasValidPreCheck() and
30+
// Is not checked after the operation
31+
not op.hasValidPostCheck() and
32+
// Permitted by exception 3
33+
not op instanceof LShiftExpr and
34+
// Permitted by exception 2 - zero case is handled in separate query
35+
not op instanceof DivExpr and
36+
not op instanceof RemExpr
37+
select op,
38+
"Operation " + op.getOperator() + " of type " + op.getType().getUnderlyingType() + " may wrap."

c/cert/src/rules/INT31-C/IntegerConversionCausesDataLoss.md

Lines changed: 361 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* @id c/cert/integer-conversion-causes-data-loss
3+
* @name INT31-C: Ensure that integer conversions do not result in lost or misinterpreted data
4+
* @description Converting an integer value to another integer type with a different sign or size
5+
* can lead to data loss or misinterpretation of the value.
6+
* @kind problem
7+
* @precision medium
8+
* @problem.severity error
9+
* @tags external/cert/id/int31-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
17+
class IntegerConversion extends Expr {
18+
private IntegralType castedToType;
19+
private Expr preConversionExpr;
20+
21+
IntegerConversion() {
22+
// This is an explicit cast
23+
castedToType = this.(Cast).getType().getUnspecifiedType() and
24+
preConversionExpr = this.(Cast).getExpr()
25+
or
26+
// Functions that internally cast an argument to unsigned char
27+
castedToType instanceof UnsignedCharType and
28+
this = preConversionExpr and
29+
exists(FunctionCall call, string name | call.getTarget().hasGlobalOrStdName(name) |
30+
name = ["ungetc", "fputc"] and
31+
this = call.getArgument(0)
32+
or
33+
name = ["memset", "memchr"] and
34+
this = call.getArgument(1)
35+
or
36+
name = "memset_s" and
37+
this = call.getArgument(2)
38+
)
39+
}
40+
41+
Expr getPreConversionExpr() { result = preConversionExpr }
42+
43+
Type getCastedToType() { result = castedToType }
44+
}
45+
46+
bindingset[value]
47+
predicate withinIntegralRange(IntegralType typ, float value) {
48+
exists(float lb, float ub, float limit |
49+
limit = 2.pow(8 * typ.getSize()) and
50+
(
51+
if typ.isUnsigned()
52+
then (
53+
lb = 0 and ub = limit - 1
54+
) else (
55+
lb = -limit / 2 and
56+
ub = (limit / 2) - 1
57+
)
58+
) and
59+
value >= lb and
60+
value <= ub
61+
)
62+
}
63+
64+
from
65+
IntegerConversion c, Expr preConversionExpr, Type castedToType, Type castedFromType,
66+
IntegralType unspecifiedCastedFromType, string typeFromMessage, float preConversionLowerBound,
67+
float preConversionUpperBound, float typeLowerBound, float typeUpperBound
68+
where
69+
not isExcluded(c, IntegerOverflowPackage::integerConversionCausesDataLossQuery()) and
70+
preConversionExpr = c.getPreConversionExpr() and
71+
castedFromType = preConversionExpr.getType() and
72+
// Casting from an integral type
73+
unspecifiedCastedFromType = castedFromType.getUnspecifiedType() and
74+
// Casting to an integral type
75+
castedToType = c.getCastedToType() and
76+
// Get the upper/lower bound of the pre-conversion expression
77+
preConversionLowerBound = lowerBound(preConversionExpr) and
78+
preConversionUpperBound = upperBound(preConversionExpr) and
79+
// Get the upper/lower bound of the target type
80+
typeLowerBound = typeLowerBound(castedToType) and
81+
typeUpperBound = typeUpperBound(castedToType) and
82+
// Where the result is not within the range of the target type
83+
(
84+
not withinIntegralRange(castedToType, preConversionLowerBound) or
85+
not withinIntegralRange(castedToType, preConversionUpperBound)
86+
) and
87+
// A conversion of `-1` to `time_t` is permitted by the standard
88+
not (
89+
c.getType().getUnspecifiedType().hasName("time_t") and
90+
preConversionExpr.getValue() = "-1"
91+
) and
92+
// Conversion to unsigned char is permitted from the range [SCHAR_MIN..UCHAR_MAX], as those can
93+
// legitimately represent characters
94+
not (
95+
c.getType().getUnspecifiedType() instanceof UnsignedCharType and
96+
lowerBound(preConversionExpr) >= typeLowerBound(any(SignedCharType s)) and
97+
upperBound(preConversionExpr) <= typeUpperBound(any(UnsignedCharType s))
98+
) and
99+
not castedToType instanceof BoolType and
100+
// Create a helpful message
101+
if castedFromType = unspecifiedCastedFromType
102+
then typeFromMessage = castedFromType.toString()
103+
else typeFromMessage = castedFromType + " (" + unspecifiedCastedFromType + ")"
104+
select c,
105+
"Conversion from " + typeFromMessage + " to " + castedToType +
106+
" may cause data loss (casting from range " + preConversionLowerBound + "..." +
107+
preConversionUpperBound + " to range " + typeLowerBound + "..." + typeUpperBound + ")."

0 commit comments

Comments
 (0)