@@ -260,11 +260,34 @@ class CStringChecker : public Checker< eval::Call,
260
260
const Expr *expr,
261
261
SVal val) const ;
262
262
263
- static ProgramStateRef InvalidateBuffer (CheckerContext &C,
264
- ProgramStateRef state,
265
- const Expr *Ex, SVal V,
266
- bool IsSourceBuffer,
267
- const Expr *Size);
263
+ // / Invalidate the destination buffer determined by characters copied.
264
+ static ProgramStateRef
265
+ invalidateDestinationBufferBySize (CheckerContext &C, ProgramStateRef S,
266
+ const Expr *BufE, SVal BufV, SVal SizeV,
267
+ QualType SizeTy);
268
+
269
+ // / Operation never overflows, do not invalidate the super region.
270
+ static ProgramStateRef invalidateDestinationBufferNeverOverflows (
271
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
272
+
273
+ // / We do not know whether the operation can overflow (e.g. size is unknown),
274
+ // / invalidate the super region and escape related pointers.
275
+ static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion (
276
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
277
+
278
+ // / Invalidate the source buffer for escaping pointers.
279
+ static ProgramStateRef invalidateSourceBuffer (CheckerContext &C,
280
+ ProgramStateRef S,
281
+ const Expr *BufE, SVal BufV);
282
+
283
+ // / @param InvalidationTraitOperations Determine how to invlidate the
284
+ // / MemRegion by setting the invalidation traits. Return true to cause pointer
285
+ // / escape, or false otherwise.
286
+ static ProgramStateRef invalidateBufferAux (
287
+ CheckerContext &C, ProgramStateRef State, const Expr *Ex, SVal V,
288
+ llvm::function_ref<bool (RegionAndSymbolInvalidationTraits &,
289
+ const MemRegion *)>
290
+ InvalidationTraitOperations);
268
291
269
292
static bool SummarizeRegion (raw_ostream &os, ASTContext &Ctx,
270
293
const MemRegion *MR);
@@ -310,10 +333,9 @@ class CStringChecker : public Checker< eval::Call,
310
333
// Return true if the destination buffer of the copy function may be in bound.
311
334
// Expects SVal of Size to be positive and unsigned.
312
335
// Expects SVal of FirstBuf to be a FieldRegion.
313
- static bool IsFirstBufInBound (CheckerContext &C,
314
- ProgramStateRef state,
315
- const Expr *FirstBuf,
316
- const Expr *Size);
336
+ static bool isFirstBufInBound (CheckerContext &C, ProgramStateRef State,
337
+ SVal BufVal, QualType BufTy, SVal LengthVal,
338
+ QualType LengthTy);
317
339
};
318
340
319
341
} // end anonymous namespace
@@ -967,43 +989,40 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
967
989
return strRegion->getStringLiteral ();
968
990
}
969
991
970
- bool CStringChecker::IsFirstBufInBound (CheckerContext &C,
971
- ProgramStateRef state,
972
- const Expr *FirstBuf,
973
- const Expr *Size) {
992
+ bool CStringChecker::isFirstBufInBound (CheckerContext &C, ProgramStateRef State,
993
+ SVal BufVal, QualType BufTy,
994
+ SVal LengthVal, QualType LengthTy) {
974
995
// If we do not know that the buffer is long enough we return 'true'.
975
996
// Otherwise the parent region of this field region would also get
976
997
// invalidated, which would lead to warnings based on an unknown state.
977
998
999
+ if (LengthVal.isUnknown ())
1000
+ return false ;
1001
+
978
1002
// Originally copied from CheckBufferAccess and CheckLocation.
979
- SValBuilder &svalBuilder = C.getSValBuilder ();
980
- ASTContext &Ctx = svalBuilder.getContext ();
981
- const LocationContext *LCtx = C.getLocationContext ();
1003
+ SValBuilder &SB = C.getSValBuilder ();
1004
+ ASTContext &Ctx = C.getASTContext ();
982
1005
983
- QualType sizeTy = Size->getType ();
984
1006
QualType PtrTy = Ctx.getPointerType (Ctx.CharTy );
985
- SVal BufVal = state->getSVal (FirstBuf, LCtx);
986
1007
987
- SVal LengthVal = state->getSVal (Size, LCtx);
988
1008
std::optional<NonLoc> Length = LengthVal.getAs <NonLoc>();
989
1009
if (!Length)
990
1010
return true ; // cf top comment.
991
1011
992
1012
// Compute the offset of the last element to be accessed: size-1.
993
- NonLoc One = svalBuilder .makeIntVal (1 , sizeTy ).castAs <NonLoc>();
994
- SVal Offset = svalBuilder .evalBinOpNN (state , BO_Sub, *Length, One, sizeTy );
1013
+ NonLoc One = SB .makeIntVal (1 , LengthTy ).castAs <NonLoc>();
1014
+ SVal Offset = SB .evalBinOpNN (State , BO_Sub, *Length, One, LengthTy );
995
1015
if (Offset.isUnknown ())
996
1016
return true ; // cf top comment
997
1017
NonLoc LastOffset = Offset.castAs <NonLoc>();
998
1018
999
1019
// Check that the first buffer is sufficiently long.
1000
- SVal BufStart = svalBuilder .evalCast (BufVal, PtrTy, FirstBuf-> getType () );
1020
+ SVal BufStart = SB .evalCast (BufVal, PtrTy, BufTy );
1001
1021
std::optional<Loc> BufLoc = BufStart.getAs <Loc>();
1002
1022
if (!BufLoc)
1003
1023
return true ; // cf top comment.
1004
1024
1005
- SVal BufEnd =
1006
- svalBuilder.evalBinOpLN (state, BO_Add, *BufLoc, LastOffset, PtrTy);
1025
+ SVal BufEnd = SB.evalBinOpLN (State, BO_Add, *BufLoc, LastOffset, PtrTy);
1007
1026
1008
1027
// Check for out of bound array element access.
1009
1028
const MemRegion *R = BufEnd.getAsRegion ();
@@ -1017,28 +1036,90 @@ bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
1017
1036
// FIXME: Does this crash when a non-standard definition
1018
1037
// of a library function is encountered?
1019
1038
assert (ER->getValueType () == C.getASTContext ().CharTy &&
1020
- " IsFirstBufInBound should only be called with char* ElementRegions" );
1039
+ " isFirstBufInBound should only be called with char* ElementRegions" );
1021
1040
1022
1041
// Get the size of the array.
1023
1042
const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion ());
1024
- DefinedOrUnknownSVal SizeDV = getDynamicExtent (state , superReg, svalBuilder );
1043
+ DefinedOrUnknownSVal SizeDV = getDynamicExtent (State , superReg, SB );
1025
1044
1026
1045
// Get the index of the accessed element.
1027
1046
DefinedOrUnknownSVal Idx = ER->getIndex ().castAs <DefinedOrUnknownSVal>();
1028
1047
1029
- ProgramStateRef StInBound = state ->assumeInBound (Idx, SizeDV, true );
1048
+ ProgramStateRef StInBound = State ->assumeInBound (Idx, SizeDV, true );
1030
1049
1031
1050
return static_cast <bool >(StInBound);
1032
1051
}
1033
1052
1034
- ProgramStateRef CStringChecker::InvalidateBuffer (CheckerContext &C,
1035
- ProgramStateRef state,
1036
- const Expr *E, SVal V,
1037
- bool IsSourceBuffer,
1038
- const Expr *Size) {
1053
+ ProgramStateRef CStringChecker::invalidateDestinationBufferBySize (
1054
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV,
1055
+ SVal SizeV, QualType SizeTy) {
1056
+ auto InvalidationTraitOperations =
1057
+ [&C, S, BufTy = BufE->getType (), BufV, SizeV,
1058
+ SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
1059
+ // If destination buffer is a field region and access is in bound, do
1060
+ // not invalidate its super region.
1061
+ if (MemRegion::FieldRegionKind == R->getKind () &&
1062
+ isFirstBufInBound (C, S, BufV, BufTy, SizeV, SizeTy)) {
1063
+ ITraits.setTrait (
1064
+ R,
1065
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
1066
+ }
1067
+ return false ;
1068
+ };
1069
+
1070
+ return invalidateBufferAux (C, S, BufE, BufV, InvalidationTraitOperations);
1071
+ }
1072
+
1073
+ ProgramStateRef
1074
+ CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion (
1075
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
1076
+ auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &,
1077
+ const MemRegion *R) {
1078
+ return isa<FieldRegion>(R);
1079
+ };
1080
+
1081
+ return invalidateBufferAux (C, S, BufE, BufV, InvalidationTraitOperations);
1082
+ }
1083
+
1084
+ ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows (
1085
+ CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
1086
+ auto InvalidationTraitOperations =
1087
+ [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
1088
+ if (MemRegion::FieldRegionKind == R->getKind ())
1089
+ ITraits.setTrait (
1090
+ R,
1091
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
1092
+ return false ;
1093
+ };
1094
+
1095
+ return invalidateBufferAux (C, S, BufE, BufV, InvalidationTraitOperations);
1096
+ }
1097
+
1098
+ ProgramStateRef CStringChecker::invalidateSourceBuffer (CheckerContext &C,
1099
+ ProgramStateRef S,
1100
+ const Expr *BufE,
1101
+ SVal BufV) {
1102
+ auto InvalidationTraitOperations =
1103
+ [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
1104
+ ITraits.setTrait (
1105
+ R->getBaseRegion (),
1106
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
1107
+ ITraits.setTrait (R,
1108
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
1109
+ return true ;
1110
+ };
1111
+
1112
+ return invalidateBufferAux (C, S, BufE, BufV, InvalidationTraitOperations);
1113
+ }
1114
+
1115
+ ProgramStateRef CStringChecker::invalidateBufferAux (
1116
+ CheckerContext &C, ProgramStateRef State, const Expr *E, SVal V,
1117
+ llvm::function_ref<bool (RegionAndSymbolInvalidationTraits &,
1118
+ const MemRegion *)>
1119
+ InvalidationTraitOperations) {
1039
1120
std::optional<Loc> L = V.getAs <Loc>();
1040
1121
if (!L)
1041
- return state ;
1122
+ return State ;
1042
1123
1043
1124
// FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
1044
1125
// some assumptions about the value that CFRefCount can't. Even so, it should
@@ -1055,37 +1136,18 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
1055
1136
1056
1137
// Invalidate this region.
1057
1138
const LocationContext *LCtx = C.getPredecessor ()->getLocationContext ();
1058
-
1059
- bool CausesPointerEscape = false ;
1060
1139
RegionAndSymbolInvalidationTraits ITraits;
1061
- // Invalidate and escape only indirect regions accessible through the source
1062
- // buffer.
1063
- if (IsSourceBuffer) {
1064
- ITraits.setTrait (R->getBaseRegion (),
1065
- RegionAndSymbolInvalidationTraits::TK_PreserveContents);
1066
- ITraits.setTrait (R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
1067
- CausesPointerEscape = true ;
1068
- } else {
1069
- const MemRegion::Kind& K = R->getKind ();
1070
- if (K == MemRegion::FieldRegionKind)
1071
- if (Size && IsFirstBufInBound (C, state, E, Size)) {
1072
- // If destination buffer is a field region and access is in bound,
1073
- // do not invalidate its super region.
1074
- ITraits.setTrait (
1075
- R,
1076
- RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
1077
- }
1078
- }
1140
+ bool CausesPointerEscape = InvalidationTraitOperations (ITraits, R);
1079
1141
1080
- return state ->invalidateRegions (R, E, C.blockCount (), LCtx,
1142
+ return State ->invalidateRegions (R, E, C.blockCount (), LCtx,
1081
1143
CausesPointerEscape, nullptr , nullptr ,
1082
1144
&ITraits);
1083
1145
}
1084
1146
1085
1147
// If we have a non-region value by chance, just remove the binding.
1086
1148
// FIXME: is this necessary or correct? This handles the non-Region
1087
1149
// cases. Is it ever valid to store to these?
1088
- return state ->killBinding (*L);
1150
+ return State ->killBinding (*L);
1089
1151
}
1090
1152
1091
1153
bool CStringChecker::SummarizeRegion (raw_ostream &os, ASTContext &Ctx,
@@ -1182,8 +1244,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
1182
1244
} else {
1183
1245
// If the destination buffer's extent is not equal to the value of
1184
1246
// third argument, just invalidate buffer.
1185
- State = InvalidateBuffer (C, State, DstBuffer, MemVal,
1186
- /* IsSourceBuffer */ false , Size);
1247
+ State = invalidateDestinationBufferBySize (C, State, DstBuffer, MemVal,
1248
+ SizeVal , Size-> getType () );
1187
1249
}
1188
1250
1189
1251
if (StateNullChar && !StateNonNullChar) {
@@ -1208,8 +1270,8 @@ bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
1208
1270
} else {
1209
1271
// If the offset is not zero and char value is not concrete, we can do
1210
1272
// nothing but invalidate the buffer.
1211
- State = InvalidateBuffer (C, State, DstBuffer, MemVal,
1212
- /* IsSourceBuffer */ false , Size);
1273
+ State = invalidateDestinationBufferBySize (C, State, DstBuffer, MemVal,
1274
+ SizeVal , Size-> getType () );
1213
1275
}
1214
1276
return true ;
1215
1277
}
@@ -1305,15 +1367,14 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const CallExpr *CE,
1305
1367
// can use LazyCompoundVals to copy the source values into the destination.
1306
1368
// This would probably remove any existing bindings past the end of the
1307
1369
// copied region, but that's still an improvement over blank invalidation.
1308
- state =
1309
- InvalidateBuffer ( C, state, Dest.Expression , C.getSVal (Dest.Expression ),
1310
- /* IsSourceBuffer */ false , Size.Expression );
1370
+ state = invalidateDestinationBufferBySize (
1371
+ C, state, Dest.Expression , C.getSVal (Dest.Expression ), sizeVal ,
1372
+ Size.Expression -> getType () );
1311
1373
1312
1374
// Invalidate the source (const-invalidation without const-pointer-escaping
1313
1375
// the address of the top-level region).
1314
- state = InvalidateBuffer (C, state, Source.Expression ,
1315
- C.getSVal (Source.Expression ),
1316
- /* IsSourceBuffer*/ true , nullptr );
1376
+ state = invalidateSourceBuffer (C, state, Source.Expression ,
1377
+ C.getSVal (Source.Expression ));
1317
1378
1318
1379
C.addTransition (state);
1319
1380
}
@@ -1985,13 +2046,13 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
1985
2046
// can use LazyCompoundVals to copy the source values into the destination.
1986
2047
// This would probably remove any existing bindings past the end of the
1987
2048
// string, but that's still an improvement over blank invalidation.
1988
- state = InvalidateBuffer (C, state, Dst.Expression , *dstRegVal,
1989
- /* IsSourceBuffer*/ false , nullptr );
2049
+ state = invalidateDestinationBufferBySize (C, state, Dst.Expression ,
2050
+ *dstRegVal, amountCopied,
2051
+ C.getASTContext ().getSizeType ());
1990
2052
1991
2053
// Invalidate the source (const-invalidation without const-pointer-escaping
1992
2054
// the address of the top-level region).
1993
- state = InvalidateBuffer (C, state, srcExpr.Expression , srcVal,
1994
- /* IsSourceBuffer*/ true , nullptr );
2055
+ state = invalidateSourceBuffer (C, state, srcExpr.Expression , srcVal);
1995
2056
1996
2057
// Set the C string length of the destination, if we know it.
1997
2058
if (IsBounded && (appendK == ConcatFnKind::none)) {
@@ -2206,8 +2267,9 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
2206
2267
2207
2268
// Invalidate the search string, representing the change of one delimiter
2208
2269
// character to NUL.
2209
- State = InvalidateBuffer (C, State, SearchStrPtr.Expression , Result,
2210
- /* IsSourceBuffer*/ false , nullptr );
2270
+ // As the replacement never overflows, do not invalidate its super region.
2271
+ State = invalidateDestinationBufferNeverOverflows (
2272
+ C, State, SearchStrPtr.Expression , Result);
2211
2273
2212
2274
// Overwrite the search string pointer. The new value is either an address
2213
2275
// further along in the same string, or NULL if there are no more tokens.
@@ -2256,8 +2318,10 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
2256
2318
// Invalidate the destination buffer
2257
2319
const Expr *Dst = CE->getArg (2 );
2258
2320
SVal DstVal = State->getSVal (Dst, LCtx);
2259
- State = InvalidateBuffer (C, State, Dst, DstVal, /* IsSource=*/ false ,
2260
- /* Size=*/ nullptr );
2321
+ // FIXME: As we do not know how many items are copied, we also invalidate the
2322
+ // super region containing the target location.
2323
+ State =
2324
+ invalidateDestinationBufferAlwaysEscapeSuperRegion (C, State, Dst, DstVal);
2261
2325
2262
2326
SValBuilder &SVB = C.getSValBuilder ();
2263
2327
0 commit comments