@@ -9,6 +9,30 @@ import semmle.code.cpp.models.interfaces.FormattingFunction
9
9
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
10
10
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
11
11
12
+ private newtype TBufferWriteEstimationReason =
13
+ TTypeBoundsAnalysis ( ) or
14
+ TValueFlowAnalysis ( )
15
+
16
+ class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
17
+ BufferWriteEstimationReason ( ) {
18
+ this = TTypeBoundsAnalysis ( ) or
19
+ this = TValueFlowAnalysis ( )
20
+ }
21
+
22
+ string toString ( ) {
23
+ this = TTypeBoundsAnalysis ( ) and result = "based on type bounds" or
24
+ this = TValueFlowAnalysis ( ) and result = "based on flow analysis of value bounds"
25
+ }
26
+
27
+ BufferWriteEstimationReason combineWith ( BufferWriteEstimationReason other ) {
28
+ ( this = TTypeBoundsAnalysis ( ) or other = TTypeBoundsAnalysis ( ) ) and result = TTypeBoundsAnalysis ( ) or
29
+ ( this = TValueFlowAnalysis ( ) and other = TValueFlowAnalysis ( ) ) and result = TValueFlowAnalysis ( )
30
+ }
31
+ }
32
+
33
+ BufferWriteEstimationReason typeBoundsAnalysis ( ) { result = TTypeBoundsAnalysis ( ) }
34
+ BufferWriteEstimationReason valueFlowAnalysis ( ) { result = TValueFlowAnalysis ( ) }
35
+
12
36
class PrintfFormatAttribute extends FormatAttribute {
13
37
PrintfFormatAttribute ( ) { this .getArchetype ( ) = [ "printf" , "__printf__" ] }
14
38
}
@@ -990,7 +1014,7 @@ class FormatLiteral extends Literal {
990
1014
* conversion specifier of this format string; has no result if this cannot
991
1015
* be determined.
992
1016
*/
993
- int getMaxConvertedLength ( int n ) {
1017
+ int getMaxConvertedLength ( int n , BufferWriteEstimationReason reason ) {
994
1018
exists ( int len |
995
1019
(
996
1020
(
@@ -1002,10 +1026,10 @@ class FormatLiteral extends Literal {
1002
1026
) and
1003
1027
(
1004
1028
this .getConversionChar ( n ) = "%" and
1005
- len = 1
1029
+ len = 1 and reason = TValueFlowAnalysis ( )
1006
1030
or
1007
1031
this .getConversionChar ( n ) .toLowerCase ( ) = "c" and
1008
- len = 1 // e.g. 'a'
1032
+ len = 1 and reason = TValueFlowAnalysis ( ) // e.g. 'a'
1009
1033
or
1010
1034
this .getConversionChar ( n ) .toLowerCase ( ) = "f" and
1011
1035
exists ( int dot , int afterdot |
@@ -1019,7 +1043,7 @@ class FormatLiteral extends Literal {
1019
1043
afterdot = 6
1020
1044
) and
1021
1045
len = 1 + 309 + dot + afterdot
1022
- ) // e.g. -1e308="-100000"...
1046
+ ) and reason = TTypeBoundsAnalysis ( ) // e.g. -1e308="-100000"...
1023
1047
or
1024
1048
this .getConversionChar ( n ) .toLowerCase ( ) = "e" and
1025
1049
exists ( int dot , int afterdot |
@@ -1033,7 +1057,7 @@ class FormatLiteral extends Literal {
1033
1057
afterdot = 6
1034
1058
) and
1035
1059
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
1036
- ) // -1e308="-1.000000e+308"
1060
+ ) and reason = TTypeBoundsAnalysis ( ) // -1e308="-1.000000e+308"
1037
1061
or
1038
1062
this .getConversionChar ( n ) .toLowerCase ( ) = "g" and
1039
1063
exists ( int dot , int afterdot |
@@ -1056,67 +1080,78 @@ class FormatLiteral extends Literal {
1056
1080
// (e.g. 123456, 0.000123456 are just OK)
1057
1081
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
1058
1082
len = ( afterdot .maximum ( 1 ) + 6 ) .maximum ( 1 + 1 + dot + afterdot + 1 + 1 + 3 )
1059
- ) // (e.g. "-1.59203e-319")
1083
+ ) and reason = TTypeBoundsAnalysis ( ) // (e.g. "-1.59203e-319")
1060
1084
or
1061
1085
this .getConversionChar ( n ) .toLowerCase ( ) = [ "d" , "i" ] and
1062
1086
// e.g. -2^31 = "-2147483648"
1063
- len =
1064
- min ( float cand |
1087
+ exists ( float typeBasedBound , float valueBasedBound |
1065
1088
// The first case handles length sub-specifiers
1066
1089
// Subtract one in the exponent because one bit is for the sign.
1067
1090
// Add 1 to account for the possible sign in the output.
1068
- cand = 1 + lengthInBase10 ( 2 .pow ( this .getIntegralDisplayType ( n ) .getSize ( ) * 8 - 1 ) )
1069
- or
1091
+ typeBasedBound = 1 + lengthInBase10 ( 2 .pow ( this .getIntegralDisplayType ( n ) .getSize ( ) * 8 - 1 ) ) and
1070
1092
// The second case uses range analysis to deduce a length that's shorter than the length
1071
1093
// of the number -2^31.
1072
- exists ( Expr arg , float lower , float upper |
1094
+ exists ( Expr arg , float lower , float upper , float typeLower , float typeUpper |
1073
1095
arg = this .getUse ( ) .getConversionArgument ( n ) and
1074
1096
lower = lowerBound ( arg .getFullyConverted ( ) ) and
1075
- upper = upperBound ( arg .getFullyConverted ( ) )
1097
+ upper = upperBound ( arg .getFullyConverted ( ) ) and
1098
+ typeLower = exprMinVal ( arg .getFullyConverted ( ) ) and
1099
+ typeUpper = exprMaxVal ( arg .getFullyConverted ( ) )
1076
1100
|
1077
- cand =
1078
- max ( int cand0 |
1101
+ valueBasedBound =
1102
+ max ( int cand |
1079
1103
// Include the sign bit in the length if it can be negative
1080
1104
(
1081
1105
if lower < 0
1082
- then cand0 = 1 + lengthInBase10 ( lower .abs ( ) )
1083
- else cand0 = lengthInBase10 ( lower )
1106
+ then cand = 1 + lengthInBase10 ( lower .abs ( ) )
1107
+ else cand = lengthInBase10 ( lower )
1084
1108
)
1085
1109
or
1086
1110
(
1087
1111
if upper < 0
1088
- then cand0 = 1 + lengthInBase10 ( upper .abs ( ) )
1089
- else cand0 = lengthInBase10 ( upper )
1112
+ then cand = 1 + lengthInBase10 ( upper .abs ( ) )
1113
+ else cand = lengthInBase10 ( upper )
1090
1114
)
1091
- )
1092
- )
1093
- )
1115
+ ) and
1116
+ (
1117
+ if lower > typeLower or upper < typeUpper
1118
+ then reason = TValueFlowAnalysis ( )
1119
+ else reason = TTypeBoundsAnalysis ( )
1120
+ )
1121
+ ) and
1122
+ len = valueBasedBound .minimum ( typeBasedBound )
1123
+ )
1094
1124
or
1095
1125
this .getConversionChar ( n ) .toLowerCase ( ) = "u" and
1096
1126
// e.g. 2^32 - 1 = "4294967295"
1097
- len =
1098
- min ( float cand |
1127
+ exists ( float typeBasedBound , float valueBasedBound |
1099
1128
// The first case handles length sub-specifiers
1100
- cand = 2 .pow ( this .getIntegralDisplayType ( n ) .getSize ( ) * 8 )
1101
- or
1129
+ typeBasedBound = lengthInBase10 ( 2 .pow ( this .getIntegralDisplayType ( n ) .getSize ( ) * 8 ) - 1 ) and
1102
1130
// The second case uses range analysis to deduce a length that's shorter than
1103
1131
// the length of the number 2^31 - 1.
1104
- exists ( Expr arg , float lower |
1132
+ exists ( Expr arg , float lower , float upper , float typeLower , float typeUpper |
1105
1133
arg = this .getUse ( ) .getConversionArgument ( n ) and
1106
- lower = lowerBound ( arg .getFullyConverted ( ) )
1134
+ lower = lowerBound ( arg .getFullyConverted ( ) ) and
1135
+ upper = upperBound ( arg .getFullyConverted ( ) ) and
1136
+ typeLower = exprMinVal ( arg .getFullyConverted ( ) ) and
1137
+ typeUpper = exprMaxVal ( arg .getFullyConverted ( ) )
1107
1138
|
1108
- cand =
1109
- max ( float cand0 |
1139
+ valueBasedBound =
1140
+ lengthInBase10 ( max ( float cand |
1110
1141
// If lower can be negative we use `(unsigned)-1` as the candidate value.
1111
1142
lower < 0 and
1112
- cand0 = 2 .pow ( any ( IntType t | t .isUnsigned ( ) ) .getSize ( ) * 8 )
1143
+ cand = 2 .pow ( any ( IntType t | t .isUnsigned ( ) ) .getSize ( ) * 8 )
1113
1144
or
1114
- cand0 = upperBound ( arg .getFullyConverted ( ) )
1115
- )
1116
- )
1117
- |
1118
- lengthInBase10 ( cand )
1119
- )
1145
+ cand = upper
1146
+ ) ) and
1147
+ (
1148
+ if lower > typeLower or upper < typeUpper
1149
+ then reason = TValueFlowAnalysis ( )
1150
+ else reason = TTypeBoundsAnalysis ( )
1151
+ )
1152
+ ) and
1153
+ len = valueBasedBound .minimum ( typeBasedBound )
1154
+ )
1120
1155
or
1121
1156
this .getConversionChar ( n ) .toLowerCase ( ) = "x" and
1122
1157
// e.g. "12345678"
@@ -1135,7 +1170,7 @@ class FormatLiteral extends Literal {
1135
1170
(
1136
1171
if this .hasAlternateFlag ( n ) then len = 2 + baseLen else len = baseLen // "0x"
1137
1172
)
1138
- )
1173
+ ) and reason = TTypeBoundsAnalysis ( )
1139
1174
or
1140
1175
this .getConversionChar ( n ) .toLowerCase ( ) = "p" and
1141
1176
exists ( PointerType ptrType , int baseLen |
@@ -1144,7 +1179,7 @@ class FormatLiteral extends Literal {
1144
1179
(
1145
1180
if this .hasAlternateFlag ( n ) then len = 2 + baseLen else len = baseLen // "0x"
1146
1181
)
1147
- )
1182
+ ) and reason = TValueFlowAnalysis ( )
1148
1183
or
1149
1184
this .getConversionChar ( n ) .toLowerCase ( ) = "o" and
1150
1185
// e.g. 2^32 - 1 = "37777777777"
@@ -1163,14 +1198,15 @@ class FormatLiteral extends Literal {
1163
1198
(
1164
1199
if this .hasAlternateFlag ( n ) then len = 1 + baseLen else len = baseLen // "0"
1165
1200
)
1166
- )
1201
+ ) and reason = TTypeBoundsAnalysis ( )
1167
1202
or
1168
1203
this .getConversionChar ( n ) .toLowerCase ( ) = "s" and
1169
1204
len =
1170
1205
min ( int v |
1171
1206
v = this .getPrecision ( n ) or
1172
1207
v = this .getUse ( ) .getFormatArgument ( n ) .( AnalysedString ) .getMaxLength ( ) - 1 // (don't count null terminator)
1173
- )
1208
+ ) and
1209
+ reason = TValueFlowAnalysis ( )
1174
1210
)
1175
1211
)
1176
1212
}
@@ -1182,10 +1218,10 @@ class FormatLiteral extends Literal {
1182
1218
* determining whether a buffer overflow is caused by long float to string
1183
1219
* conversions.
1184
1220
*/
1185
- int getMaxConvertedLengthLimited ( int n ) {
1221
+ int getMaxConvertedLengthLimited ( int n , BufferWriteEstimationReason reason ) {
1186
1222
if this .getConversionChar ( n ) .toLowerCase ( ) = "f"
1187
- then result = this .getMaxConvertedLength ( n ) .minimum ( 8 )
1188
- else result = this .getMaxConvertedLength ( n )
1223
+ then result = this .getMaxConvertedLength ( n , reason ) .minimum ( 8 )
1224
+ else result = this .getMaxConvertedLength ( n , reason )
1189
1225
}
1190
1226
1191
1227
/**
@@ -1225,35 +1261,49 @@ class FormatLiteral extends Literal {
1225
1261
)
1226
1262
}
1227
1263
1228
- private int getMaxConvertedLengthAfter ( int n ) {
1264
+ private int getMaxConvertedLengthAfter ( int n , BufferWriteEstimationReason reason ) {
1229
1265
if n = this .getNumConvSpec ( )
1230
- then result = this .getConstantSuffix ( ) .length ( ) + 1
1266
+ then result = this .getConstantSuffix ( ) .length ( ) + 1 and reason = TValueFlowAnalysis ( )
1231
1267
else
1232
- result =
1233
- this .getConstantPart ( n ) .length ( ) + this .getMaxConvertedLength ( n ) +
1234
- this .getMaxConvertedLengthAfter ( n + 1 )
1268
+ exists ( BufferWriteEstimationReason headReason , BufferWriteEstimationReason tailReason |
1269
+ result =
1270
+ this .getConstantPart ( n ) .length ( ) + this .getMaxConvertedLength ( n , headReason ) +
1271
+ this .getMaxConvertedLengthAfter ( n + 1 , tailReason ) and
1272
+ reason = headReason .combineWith ( tailReason )
1273
+ )
1235
1274
}
1236
1275
1237
- private int getMaxConvertedLengthAfterLimited ( int n ) {
1276
+ private int getMaxConvertedLengthAfterLimited ( int n , BufferWriteEstimationReason reason ) {
1238
1277
if n = this .getNumConvSpec ( )
1239
- then result = this .getConstantSuffix ( ) .length ( ) + 1
1278
+ then result = this .getConstantSuffix ( ) .length ( ) + 1 and reason = TValueFlowAnalysis ( )
1240
1279
else
1280
+ exists ( BufferWriteEstimationReason headReason , BufferWriteEstimationReason tailReason |
1241
1281
result =
1242
- this .getConstantPart ( n ) .length ( ) + this .getMaxConvertedLengthLimited ( n ) +
1243
- this .getMaxConvertedLengthAfterLimited ( n + 1 )
1282
+ this .getConstantPart ( n ) .length ( ) + this .getMaxConvertedLengthLimited ( n , headReason ) +
1283
+ this .getMaxConvertedLengthAfterLimited ( n + 1 , tailReason ) and
1284
+ reason = headReason .combineWith ( tailReason )
1285
+ )
1244
1286
}
1245
1287
1246
1288
/**
1247
1289
* Gets the maximum length of the string that can be produced by this format
1248
1290
* string. Has no result if this cannot be determined.
1249
1291
*/
1250
- int getMaxConvertedLength ( ) { result = this .getMaxConvertedLengthAfter ( 0 ) }
1292
+ int getMaxConvertedLength ( ) { result = this .getMaxConvertedLengthAfter ( 0 , _ ) }
1251
1293
1252
1294
/**
1253
1295
* Gets the maximum length of the string that can be produced by this format
1254
1296
* string, except that float to string conversions are assumed to be 8
1255
1297
* characters. This is helpful for determining whether a buffer overflow
1256
1298
* is caused by long float to string conversions.
1257
1299
*/
1258
- int getMaxConvertedLengthLimited ( ) { result = this .getMaxConvertedLengthAfterLimited ( 0 ) }
1300
+ int getMaxConvertedLengthLimited ( ) { result = this .getMaxConvertedLengthAfterLimited ( 0 , _) }
1301
+
1302
+ int getMaxConvertedLengthWithReason ( BufferWriteEstimationReason reason ) {
1303
+ result = this .getMaxConvertedLengthAfter ( 0 , reason )
1304
+ }
1305
+
1306
+ int getMaxConvertedLengthLimitedWithReason ( BufferWriteEstimationReason reason ) {
1307
+ result = this .getMaxConvertedLengthAfterLimited ( 0 , reason )
1308
+ }
1259
1309
}
0 commit comments