@@ -163,6 +163,7 @@ class CStringChecker
163
163
{{CDM::CLibrary, {" strcasecmp" }, 2 }, &CStringChecker::evalStrcasecmp},
164
164
{{CDM::CLibrary, {" strncasecmp" }, 3 }, &CStringChecker::evalStrncasecmp},
165
165
{{CDM::CLibrary, {" strsep" }, 2 }, &CStringChecker::evalStrsep},
166
+ {{CDM::CLibrary, {" strxfrm" }, 3 }, &CStringChecker::evalStrxfrm},
166
167
{{CDM::CLibrary, {" bcopy" }, 3 }, &CStringChecker::evalBcopy},
167
168
{{CDM::CLibrary, {" bcmp" }, 3 },
168
169
std::bind (&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
@@ -211,6 +212,8 @@ class CStringChecker
211
212
bool ReturnEnd, bool IsBounded, ConcatFnKind appendK,
212
213
bool returnPtr = true ) const ;
213
214
215
+ void evalStrxfrm (CheckerContext &C, const CallEvent &Call) const ;
216
+
214
217
void evalStrcat (CheckerContext &C, const CallEvent &Call) const ;
215
218
void evalStrncat (CheckerContext &C, const CallEvent &Call) const ;
216
219
void evalStrlcat (CheckerContext &C, const CallEvent &Call) const ;
@@ -2243,6 +2246,106 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,
2243
2246
C.addTransition (state);
2244
2247
}
2245
2248
2249
+ void CStringChecker::evalStrxfrm (CheckerContext &C,
2250
+ const CallEvent &Call) const {
2251
+ // size_t strxfrm(char *dest, const char *src, size_t n);
2252
+ CurrentFunctionDescription = " locale transformation function" ;
2253
+
2254
+ ProgramStateRef State = C.getState ();
2255
+ const LocationContext *LCtx = C.getLocationContext ();
2256
+ SValBuilder &SVB = C.getSValBuilder ();
2257
+
2258
+ // Get arguments
2259
+ DestinationArgExpr Dest = {{Call.getArgExpr (0 ), 0 }};
2260
+ SourceArgExpr Source = {{Call.getArgExpr (1 ), 1 }};
2261
+ SizeArgExpr Size = {{Call.getArgExpr (2 ), 2 }};
2262
+
2263
+ // `src` can never be null
2264
+ SVal SrcVal = State->getSVal (Source.Expression , LCtx);
2265
+ State = checkNonNull (C, State, Source, SrcVal);
2266
+ if (!State)
2267
+ return ;
2268
+
2269
+ // Buffer must not overlap
2270
+ State = CheckOverlap (C, State, Size, Dest, Source, CK_Regular);
2271
+ if (!State)
2272
+ return ;
2273
+
2274
+ // The function returns an implementation-defined length needed for
2275
+ // transformation
2276
+ SVal RetVal = SVB.conjureSymbolVal (Call, C.blockCount ());
2277
+
2278
+ auto BindReturnAndTransition = [&RetVal, &Call, LCtx,
2279
+ &C](ProgramStateRef State) {
2280
+ if (State) {
2281
+ State = State->BindExpr (Call.getOriginExpr (), LCtx, RetVal);
2282
+ C.addTransition (State);
2283
+ }
2284
+ };
2285
+
2286
+ // Check if size is zero
2287
+ SVal SizeVal = State->getSVal (Size.Expression , LCtx);
2288
+ QualType SizeTy = Size.Expression ->getType ();
2289
+
2290
+ auto [StateZeroSize, StateSizeNonZero] =
2291
+ assumeZero (C, State, SizeVal, SizeTy);
2292
+
2293
+ // We can't assume anything about size, just bind the return value and be done
2294
+ if (!StateZeroSize && !StateSizeNonZero)
2295
+ return BindReturnAndTransition (State);
2296
+
2297
+ // If `n` is 0, we just return the implementation defined length
2298
+ if (StateZeroSize && !StateSizeNonZero)
2299
+ return BindReturnAndTransition (StateZeroSize);
2300
+
2301
+ // If `n` is not 0, `dest` can not be null.
2302
+ SVal DestVal = StateSizeNonZero->getSVal (Dest.Expression , LCtx);
2303
+ StateSizeNonZero = checkNonNull (C, StateSizeNonZero, Dest, DestVal);
2304
+ if (!StateSizeNonZero)
2305
+ return ;
2306
+
2307
+ // Check that we can write to the destination buffer
2308
+ StateSizeNonZero = CheckBufferAccess (C, StateSizeNonZero, Dest, Size,
2309
+ AccessKind::write, CK_Regular);
2310
+ if (!StateSizeNonZero)
2311
+ return ;
2312
+
2313
+ // Success: return value < `n`
2314
+ // Failure: return value >= `n`
2315
+ auto ComparisonVal = SVB.evalBinOp (StateSizeNonZero, BO_LT, RetVal, SizeVal,
2316
+ SVB.getConditionType ())
2317
+ .getAs <DefinedOrUnknownSVal>();
2318
+ if (!ComparisonVal) {
2319
+ // Fallback: invalidate the buffer.
2320
+ StateSizeNonZero = invalidateDestinationBufferBySize (
2321
+ C, StateSizeNonZero, Dest.Expression , Call.getCFGElementRef (), DestVal,
2322
+ SizeVal, Size.Expression ->getType ());
2323
+ return BindReturnAndTransition (StateSizeNonZero);
2324
+ }
2325
+
2326
+ auto [StateSuccess, StateFailure] = StateSizeNonZero->assume (*ComparisonVal);
2327
+
2328
+ if (StateSuccess) {
2329
+ // The transformation invalidated the buffer.
2330
+ StateSuccess = invalidateDestinationBufferBySize (
2331
+ C, StateSuccess, Dest.Expression , Call.getCFGElementRef (), DestVal,
2332
+ SizeVal, Size.Expression ->getType ());
2333
+ BindReturnAndTransition (StateSuccess);
2334
+ // Fallthrough: We also want to add a transition to the failure state below.
2335
+ }
2336
+
2337
+ if (StateFailure) {
2338
+ // `dest` buffer content is undefined
2339
+ if (auto DestLoc = DestVal.getAs <loc::MemRegionVal>()) {
2340
+ StateFailure = StateFailure->killBinding (*DestLoc);
2341
+ StateFailure =
2342
+ StateFailure->bindDefaultInitial (*DestLoc, UndefinedVal{}, LCtx);
2343
+ }
2344
+
2345
+ BindReturnAndTransition (StateFailure);
2346
+ }
2347
+ }
2348
+
2246
2349
void CStringChecker::evalStrcmp (CheckerContext &C,
2247
2350
const CallEvent &Call) const {
2248
2351
// int strcmp(const char *s1, const char *s2);
0 commit comments