10b57cec5SDimitry Andric //= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines CStringChecker, which is an assortment of checks on calls
100b57cec5SDimitry Andric // to functions in <string.h>.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric
140b57cec5SDimitry Andric #include "InterCheckerAPI.h"
15*0fca6ea1SDimitry Andric #include "clang/AST/OperationKinds.h"
1606c3fb27SDimitry Andric #include "clang/Basic/Builtins.h"
170b57cec5SDimitry Andric #include "clang/Basic/CharInfo.h"
185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
22349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
26*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
270b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
28*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
29*0fca6ea1SDimitry Andric #include "llvm/ADT/APSInt.h"
300b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
315ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h"
32*0fca6ea1SDimitry Andric #include "llvm/Support/Casting.h"
330b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
34972a253aSDimitry Andric #include <functional>
35bdd1243dSDimitry Andric #include <optional>
360b57cec5SDimitry Andric
370b57cec5SDimitry Andric using namespace clang;
380b57cec5SDimitry Andric using namespace ento;
39972a253aSDimitry Andric using namespace std::placeholders;
400b57cec5SDimitry Andric
410b57cec5SDimitry Andric namespace {
425ffd83dbSDimitry Andric struct AnyArgExpr {
435ffd83dbSDimitry Andric const Expr *Expression;
445ffd83dbSDimitry Andric unsigned ArgumentIndex;
455ffd83dbSDimitry Andric };
4606c3fb27SDimitry Andric struct SourceArgExpr : AnyArgExpr {};
4706c3fb27SDimitry Andric struct DestinationArgExpr : AnyArgExpr {};
4806c3fb27SDimitry Andric struct SizeArgExpr : AnyArgExpr {};
495ffd83dbSDimitry Andric
505ffd83dbSDimitry Andric using ErrorMessage = SmallString<128>;
515ffd83dbSDimitry Andric enum class AccessKind { write, read };
525ffd83dbSDimitry Andric
createOutOfBoundErrorMsg(StringRef FunctionDescription,AccessKind Access)535ffd83dbSDimitry Andric static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,
545ffd83dbSDimitry Andric AccessKind Access) {
555ffd83dbSDimitry Andric ErrorMessage Message;
565ffd83dbSDimitry Andric llvm::raw_svector_ostream Os(Message);
575ffd83dbSDimitry Andric
585ffd83dbSDimitry Andric // Function classification like: Memory copy function
595ffd83dbSDimitry Andric Os << toUppercase(FunctionDescription.front())
605ffd83dbSDimitry Andric << &FunctionDescription.data()[1];
615ffd83dbSDimitry Andric
625ffd83dbSDimitry Andric if (Access == AccessKind::write) {
635ffd83dbSDimitry Andric Os << " overflows the destination buffer";
645ffd83dbSDimitry Andric } else { // read access
655ffd83dbSDimitry Andric Os << " accesses out-of-bound array element";
665ffd83dbSDimitry Andric }
675ffd83dbSDimitry Andric
685ffd83dbSDimitry Andric return Message;
695ffd83dbSDimitry Andric }
705ffd83dbSDimitry Andric
71480093f4SDimitry Andric enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };
72bdd1243dSDimitry Andric
73bdd1243dSDimitry Andric enum class CharKind { Regular = 0, Wide };
74bdd1243dSDimitry Andric constexpr CharKind CK_Regular = CharKind::Regular;
75bdd1243dSDimitry Andric constexpr CharKind CK_Wide = CharKind::Wide;
76bdd1243dSDimitry Andric
getCharPtrType(ASTContext & Ctx,CharKind CK)77bdd1243dSDimitry Andric static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {
78bdd1243dSDimitry Andric return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy
79bdd1243dSDimitry Andric : Ctx.WideCharTy);
80bdd1243dSDimitry Andric }
81bdd1243dSDimitry Andric
820b57cec5SDimitry Andric class CStringChecker : public Checker< eval::Call,
830b57cec5SDimitry Andric check::PreStmt<DeclStmt>,
840b57cec5SDimitry Andric check::LiveSymbols,
850b57cec5SDimitry Andric check::DeadSymbols,
860b57cec5SDimitry Andric check::RegionChanges
870b57cec5SDimitry Andric > {
880b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
8981ad6265SDimitry Andric BT_NotCString, BT_AdditionOverflow, BT_UninitRead;
900b57cec5SDimitry Andric
9106c3fb27SDimitry Andric mutable const char *CurrentFunctionDescription = nullptr;
920b57cec5SDimitry Andric
930b57cec5SDimitry Andric public:
940b57cec5SDimitry Andric /// The filter is used to filter out the diagnostics which are not enabled by
950b57cec5SDimitry Andric /// the user.
960b57cec5SDimitry Andric struct CStringChecksFilter {
9781ad6265SDimitry Andric bool CheckCStringNullArg = false;
9881ad6265SDimitry Andric bool CheckCStringOutOfBounds = false;
9981ad6265SDimitry Andric bool CheckCStringBufferOverlap = false;
10081ad6265SDimitry Andric bool CheckCStringNotNullTerm = false;
10181ad6265SDimitry Andric bool CheckCStringUninitializedRead = false;
1020b57cec5SDimitry Andric
103a7dea167SDimitry Andric CheckerNameRef CheckNameCStringNullArg;
104a7dea167SDimitry Andric CheckerNameRef CheckNameCStringOutOfBounds;
105a7dea167SDimitry Andric CheckerNameRef CheckNameCStringBufferOverlap;
106a7dea167SDimitry Andric CheckerNameRef CheckNameCStringNotNullTerm;
10781ad6265SDimitry Andric CheckerNameRef CheckNameCStringUninitializedRead;
1080b57cec5SDimitry Andric };
1090b57cec5SDimitry Andric
1100b57cec5SDimitry Andric CStringChecksFilter Filter;
1110b57cec5SDimitry Andric
getTag()1120b57cec5SDimitry Andric static void *getTag() { static int tag; return &tag; }
1130b57cec5SDimitry Andric
1140b57cec5SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const;
1150b57cec5SDimitry Andric void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
1160b57cec5SDimitry Andric void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;
1170b57cec5SDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
1180b57cec5SDimitry Andric
1190b57cec5SDimitry Andric ProgramStateRef
1200b57cec5SDimitry Andric checkRegionChanges(ProgramStateRef state,
1210b57cec5SDimitry Andric const InvalidatedSymbols *,
1220b57cec5SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
1230b57cec5SDimitry Andric ArrayRef<const MemRegion *> Regions,
1240b57cec5SDimitry Andric const LocationContext *LCtx,
1250b57cec5SDimitry Andric const CallEvent *Call) const;
1260b57cec5SDimitry Andric
127972a253aSDimitry Andric using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,
128647cbc5dSDimitry Andric const CallEvent &)>;
129972a253aSDimitry Andric
1300b57cec5SDimitry Andric CallDescriptionMap<FnCheck> Callbacks = {
131*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"memcpy"}, 3},
132bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)},
133*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3},
134bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)},
135*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"mempcpy"}, 3},
136bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)},
137*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"wmempcpy"}, 3},
138bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)},
139*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"memcmp"}, 3},
140bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
141*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"wmemcmp"}, 3},
142bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)},
143*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"memmove"}, 3},
144bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)},
145*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"wmemmove"}, 3},
146bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)},
147*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"memset"}, 3},
148*0fca6ea1SDimitry Andric &CStringChecker::evalMemset},
149*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"explicit_memset"}, 3}, &CStringChecker::evalMemset},
150*0fca6ea1SDimitry Andric // FIXME: C23 introduces 'memset_explicit', maybe also model that
151*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strcpy"}, 2},
152*0fca6ea1SDimitry Andric &CStringChecker::evalStrcpy},
153*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strncpy"}, 3},
154*0fca6ea1SDimitry Andric &CStringChecker::evalStrncpy},
155*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"stpcpy"}, 2},
156*0fca6ea1SDimitry Andric &CStringChecker::evalStpcpy},
157*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strlcpy"}, 3},
158*0fca6ea1SDimitry Andric &CStringChecker::evalStrlcpy},
159*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strcat"}, 2},
160*0fca6ea1SDimitry Andric &CStringChecker::evalStrcat},
161*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strncat"}, 3},
162*0fca6ea1SDimitry Andric &CStringChecker::evalStrncat},
163*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strlcat"}, 3},
164*0fca6ea1SDimitry Andric &CStringChecker::evalStrlcat},
165*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strlen"}, 1},
166*0fca6ea1SDimitry Andric &CStringChecker::evalstrLength},
167*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"wcslen"}, 1}, &CStringChecker::evalstrLength},
168*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"strnlen"}, 2},
169*0fca6ea1SDimitry Andric &CStringChecker::evalstrnLength},
170*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength},
171*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"strcmp"}, 2}, &CStringChecker::evalStrcmp},
172*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"strncmp"}, 3}, &CStringChecker::evalStrncmp},
173*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp},
174*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"strncasecmp"}, 3}, &CStringChecker::evalStrncasecmp},
175*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"strsep"}, 2}, &CStringChecker::evalStrsep},
176*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"bcopy"}, 3}, &CStringChecker::evalBcopy},
177*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"bcmp"}, 3},
178bdd1243dSDimitry Andric std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},
179*0fca6ea1SDimitry Andric {{CDM::CLibrary, {"bzero"}, 2}, &CStringChecker::evalBzero},
180*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"explicit_bzero"}, 2},
181*0fca6ea1SDimitry Andric &CStringChecker::evalBzero},
182*0fca6ea1SDimitry Andric
183*0fca6ea1SDimitry Andric // When recognizing calls to the following variadic functions, we accept
184*0fca6ea1SDimitry Andric // any number of arguments in the call (std::nullopt = accept any
185*0fca6ea1SDimitry Andric // number), but check that in the declaration there are 2 and 3
186*0fca6ea1SDimitry Andric // parameters respectively. (Note that the parameter count does not
187*0fca6ea1SDimitry Andric // include the "...". Calls where the number of arguments is too small
188*0fca6ea1SDimitry Andric // will be discarded by the callback.)
189*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"sprintf"}, std::nullopt, 2},
190*0fca6ea1SDimitry Andric &CStringChecker::evalSprintf},
191*0fca6ea1SDimitry Andric {{CDM::CLibraryMaybeHardened, {"snprintf"}, std::nullopt, 3},
192*0fca6ea1SDimitry Andric &CStringChecker::evalSnprintf},
1930b57cec5SDimitry Andric };
1940b57cec5SDimitry Andric
1950b57cec5SDimitry Andric // These require a bit of special handling.
196*0fca6ea1SDimitry Andric CallDescription StdCopy{CDM::SimpleFunc, {"std", "copy"}, 3},
197*0fca6ea1SDimitry Andric StdCopyBackward{CDM::SimpleFunc, {"std", "copy_backward"}, 3};
1980b57cec5SDimitry Andric
1990b57cec5SDimitry Andric FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
200647cbc5dSDimitry Andric void evalMemcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
201647cbc5dSDimitry Andric void evalMempcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
202647cbc5dSDimitry Andric void evalMemmove(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
203647cbc5dSDimitry Andric void evalBcopy(CheckerContext &C, const CallEvent &Call) const;
204647cbc5dSDimitry Andric void evalCopyCommon(CheckerContext &C, const CallEvent &Call,
2055ffd83dbSDimitry Andric ProgramStateRef state, SizeArgExpr Size,
2065ffd83dbSDimitry Andric DestinationArgExpr Dest, SourceArgExpr Source,
207bdd1243dSDimitry Andric bool Restricted, bool IsMempcpy, CharKind CK) const;
2080b57cec5SDimitry Andric
209647cbc5dSDimitry Andric void evalMemcmp(CheckerContext &C, const CallEvent &Call, CharKind CK) const;
2100b57cec5SDimitry Andric
211647cbc5dSDimitry Andric void evalstrLength(CheckerContext &C, const CallEvent &Call) const;
212647cbc5dSDimitry Andric void evalstrnLength(CheckerContext &C, const CallEvent &Call) const;
213647cbc5dSDimitry Andric void evalstrLengthCommon(CheckerContext &C, const CallEvent &Call,
2140b57cec5SDimitry Andric bool IsStrnlen = false) const;
2150b57cec5SDimitry Andric
216647cbc5dSDimitry Andric void evalStrcpy(CheckerContext &C, const CallEvent &Call) const;
217647cbc5dSDimitry Andric void evalStrncpy(CheckerContext &C, const CallEvent &Call) const;
218647cbc5dSDimitry Andric void evalStpcpy(CheckerContext &C, const CallEvent &Call) const;
219647cbc5dSDimitry Andric void evalStrlcpy(CheckerContext &C, const CallEvent &Call) const;
220647cbc5dSDimitry Andric void evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,
221647cbc5dSDimitry Andric bool ReturnEnd, bool IsBounded, ConcatFnKind appendK,
2220b57cec5SDimitry Andric bool returnPtr = true) const;
2230b57cec5SDimitry Andric
224647cbc5dSDimitry Andric void evalStrcat(CheckerContext &C, const CallEvent &Call) const;
225647cbc5dSDimitry Andric void evalStrncat(CheckerContext &C, const CallEvent &Call) const;
226647cbc5dSDimitry Andric void evalStrlcat(CheckerContext &C, const CallEvent &Call) const;
2270b57cec5SDimitry Andric
228647cbc5dSDimitry Andric void evalStrcmp(CheckerContext &C, const CallEvent &Call) const;
229647cbc5dSDimitry Andric void evalStrncmp(CheckerContext &C, const CallEvent &Call) const;
230647cbc5dSDimitry Andric void evalStrcasecmp(CheckerContext &C, const CallEvent &Call) const;
231647cbc5dSDimitry Andric void evalStrncasecmp(CheckerContext &C, const CallEvent &Call) const;
232647cbc5dSDimitry Andric void evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,
233647cbc5dSDimitry Andric bool IsBounded = false, bool IgnoreCase = false) const;
2340b57cec5SDimitry Andric
235647cbc5dSDimitry Andric void evalStrsep(CheckerContext &C, const CallEvent &Call) const;
2360b57cec5SDimitry Andric
237647cbc5dSDimitry Andric void evalStdCopy(CheckerContext &C, const CallEvent &Call) const;
238647cbc5dSDimitry Andric void evalStdCopyBackward(CheckerContext &C, const CallEvent &Call) const;
239647cbc5dSDimitry Andric void evalStdCopyCommon(CheckerContext &C, const CallEvent &Call) const;
240647cbc5dSDimitry Andric void evalMemset(CheckerContext &C, const CallEvent &Call) const;
241647cbc5dSDimitry Andric void evalBzero(CheckerContext &C, const CallEvent &Call) const;
2420b57cec5SDimitry Andric
243647cbc5dSDimitry Andric void evalSprintf(CheckerContext &C, const CallEvent &Call) const;
244647cbc5dSDimitry Andric void evalSnprintf(CheckerContext &C, const CallEvent &Call) const;
245647cbc5dSDimitry Andric void evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
246*0fca6ea1SDimitry Andric bool IsBounded) const;
24706c3fb27SDimitry Andric
2480b57cec5SDimitry Andric // Utility methods
2490b57cec5SDimitry Andric std::pair<ProgramStateRef , ProgramStateRef >
2500b57cec5SDimitry Andric static assumeZero(CheckerContext &C,
2510b57cec5SDimitry Andric ProgramStateRef state, SVal V, QualType Ty);
2520b57cec5SDimitry Andric
2530b57cec5SDimitry Andric static ProgramStateRef setCStringLength(ProgramStateRef state,
2540b57cec5SDimitry Andric const MemRegion *MR,
2550b57cec5SDimitry Andric SVal strLength);
2560b57cec5SDimitry Andric static SVal getCStringLengthForRegion(CheckerContext &C,
2570b57cec5SDimitry Andric ProgramStateRef &state,
2580b57cec5SDimitry Andric const Expr *Ex,
2590b57cec5SDimitry Andric const MemRegion *MR,
2600b57cec5SDimitry Andric bool hypothetical);
2610b57cec5SDimitry Andric SVal getCStringLength(CheckerContext &C,
2620b57cec5SDimitry Andric ProgramStateRef &state,
2630b57cec5SDimitry Andric const Expr *Ex,
2640b57cec5SDimitry Andric SVal Buf,
2650b57cec5SDimitry Andric bool hypothetical = false) const;
2660b57cec5SDimitry Andric
2670b57cec5SDimitry Andric const StringLiteral *getCStringLiteral(CheckerContext &C,
2680b57cec5SDimitry Andric ProgramStateRef &state,
2690b57cec5SDimitry Andric const Expr *expr,
2700b57cec5SDimitry Andric SVal val) const;
2710b57cec5SDimitry Andric
27206c3fb27SDimitry Andric /// Invalidate the destination buffer determined by characters copied.
27306c3fb27SDimitry Andric static ProgramStateRef
27406c3fb27SDimitry Andric invalidateDestinationBufferBySize(CheckerContext &C, ProgramStateRef S,
27506c3fb27SDimitry Andric const Expr *BufE, SVal BufV, SVal SizeV,
27606c3fb27SDimitry Andric QualType SizeTy);
27706c3fb27SDimitry Andric
27806c3fb27SDimitry Andric /// Operation never overflows, do not invalidate the super region.
27906c3fb27SDimitry Andric static ProgramStateRef invalidateDestinationBufferNeverOverflows(
28006c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
28106c3fb27SDimitry Andric
28206c3fb27SDimitry Andric /// We do not know whether the operation can overflow (e.g. size is unknown),
28306c3fb27SDimitry Andric /// invalidate the super region and escape related pointers.
28406c3fb27SDimitry Andric static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion(
28506c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV);
28606c3fb27SDimitry Andric
28706c3fb27SDimitry Andric /// Invalidate the source buffer for escaping pointers.
28806c3fb27SDimitry Andric static ProgramStateRef invalidateSourceBuffer(CheckerContext &C,
28906c3fb27SDimitry Andric ProgramStateRef S,
29006c3fb27SDimitry Andric const Expr *BufE, SVal BufV);
29106c3fb27SDimitry Andric
29206c3fb27SDimitry Andric /// @param InvalidationTraitOperations Determine how to invlidate the
29306c3fb27SDimitry Andric /// MemRegion by setting the invalidation traits. Return true to cause pointer
29406c3fb27SDimitry Andric /// escape, or false otherwise.
29506c3fb27SDimitry Andric static ProgramStateRef invalidateBufferAux(
29606c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef State, const Expr *Ex, SVal V,
29706c3fb27SDimitry Andric llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,
29806c3fb27SDimitry Andric const MemRegion *)>
29906c3fb27SDimitry Andric InvalidationTraitOperations);
3000b57cec5SDimitry Andric
3010b57cec5SDimitry Andric static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
3020b57cec5SDimitry Andric const MemRegion *MR);
3030b57cec5SDimitry Andric
3040b57cec5SDimitry Andric static bool memsetAux(const Expr *DstBuffer, SVal CharE,
3050b57cec5SDimitry Andric const Expr *Size, CheckerContext &C,
3060b57cec5SDimitry Andric ProgramStateRef &State);
3070b57cec5SDimitry Andric
3080b57cec5SDimitry Andric // Re-usable checks
3095ffd83dbSDimitry Andric ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef State,
3105ffd83dbSDimitry Andric AnyArgExpr Arg, SVal l) const;
311*0fca6ea1SDimitry Andric // Check whether the origin region behind \p Element (like the actual array
312*0fca6ea1SDimitry Andric // region \p Element is from) is initialized.
313*0fca6ea1SDimitry Andric ProgramStateRef checkInit(CheckerContext &C, ProgramStateRef state,
314*0fca6ea1SDimitry Andric AnyArgExpr Buffer, SVal Element, SVal Size) const;
3155ffd83dbSDimitry Andric ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
3165ffd83dbSDimitry Andric AnyArgExpr Buffer, SVal Element,
317bdd1243dSDimitry Andric AccessKind Access,
318bdd1243dSDimitry Andric CharKind CK = CharKind::Regular) const;
3195ffd83dbSDimitry Andric ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
3205ffd83dbSDimitry Andric AnyArgExpr Buffer, SizeArgExpr Size,
321972a253aSDimitry Andric AccessKind Access,
322bdd1243dSDimitry Andric CharKind CK = CharKind::Regular) const;
3235ffd83dbSDimitry Andric ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
3245ffd83dbSDimitry Andric SizeArgExpr Size, AnyArgExpr First,
325bdd1243dSDimitry Andric AnyArgExpr Second,
326bdd1243dSDimitry Andric CharKind CK = CharKind::Regular) const;
3270b57cec5SDimitry Andric void emitOverlapBug(CheckerContext &C,
3280b57cec5SDimitry Andric ProgramStateRef state,
3290b57cec5SDimitry Andric const Stmt *First,
3300b57cec5SDimitry Andric const Stmt *Second) const;
3310b57cec5SDimitry Andric
3320b57cec5SDimitry Andric void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S,
3330b57cec5SDimitry Andric StringRef WarningMsg) const;
3340b57cec5SDimitry Andric void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State,
3350b57cec5SDimitry Andric const Stmt *S, StringRef WarningMsg) const;
3360b57cec5SDimitry Andric void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
3370b57cec5SDimitry Andric const Stmt *S, StringRef WarningMsg) const;
3380b57cec5SDimitry Andric void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const;
33981ad6265SDimitry Andric void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State,
340*0fca6ea1SDimitry Andric const Expr *E, StringRef Msg) const;
3410b57cec5SDimitry Andric ProgramStateRef checkAdditionOverflow(CheckerContext &C,
3420b57cec5SDimitry Andric ProgramStateRef state,
3430b57cec5SDimitry Andric NonLoc left,
3440b57cec5SDimitry Andric NonLoc right) const;
3450b57cec5SDimitry Andric
3460b57cec5SDimitry Andric // Return true if the destination buffer of the copy function may be in bound.
3470b57cec5SDimitry Andric // Expects SVal of Size to be positive and unsigned.
3480b57cec5SDimitry Andric // Expects SVal of FirstBuf to be a FieldRegion.
34906c3fb27SDimitry Andric static bool isFirstBufInBound(CheckerContext &C, ProgramStateRef State,
35006c3fb27SDimitry Andric SVal BufVal, QualType BufTy, SVal LengthVal,
35106c3fb27SDimitry Andric QualType LengthTy);
3520b57cec5SDimitry Andric };
3530b57cec5SDimitry Andric
3540b57cec5SDimitry Andric } //end anonymous namespace
3550b57cec5SDimitry Andric
REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength,const MemRegion *,SVal)3560b57cec5SDimitry Andric REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
3570b57cec5SDimitry Andric
3580b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3590b57cec5SDimitry Andric // Individual checks and utility methods.
3600b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3610b57cec5SDimitry Andric
3620b57cec5SDimitry Andric std::pair<ProgramStateRef, ProgramStateRef>
363*0fca6ea1SDimitry Andric CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef State, SVal V,
3640b57cec5SDimitry Andric QualType Ty) {
365bdd1243dSDimitry Andric std::optional<DefinedSVal> val = V.getAs<DefinedSVal>();
3660b57cec5SDimitry Andric if (!val)
367*0fca6ea1SDimitry Andric return std::pair<ProgramStateRef, ProgramStateRef>(State, State);
3680b57cec5SDimitry Andric
3690b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
3700b57cec5SDimitry Andric DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty);
371*0fca6ea1SDimitry Andric return State->assume(svalBuilder.evalEQ(State, *val, zero));
3720b57cec5SDimitry Andric }
3730b57cec5SDimitry Andric
checkNonNull(CheckerContext & C,ProgramStateRef State,AnyArgExpr Arg,SVal l) const3740b57cec5SDimitry Andric ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
3755ffd83dbSDimitry Andric ProgramStateRef State,
3765ffd83dbSDimitry Andric AnyArgExpr Arg, SVal l) const {
3770b57cec5SDimitry Andric // If a previous check has failed, propagate the failure.
3785ffd83dbSDimitry Andric if (!State)
3790b57cec5SDimitry Andric return nullptr;
3800b57cec5SDimitry Andric
3810b57cec5SDimitry Andric ProgramStateRef stateNull, stateNonNull;
3825ffd83dbSDimitry Andric std::tie(stateNull, stateNonNull) =
3835ffd83dbSDimitry Andric assumeZero(C, State, l, Arg.Expression->getType());
3840b57cec5SDimitry Andric
3850b57cec5SDimitry Andric if (stateNull && !stateNonNull) {
3860b57cec5SDimitry Andric if (Filter.CheckCStringNullArg) {
3870b57cec5SDimitry Andric SmallString<80> buf;
388a7dea167SDimitry Andric llvm::raw_svector_ostream OS(buf);
3890b57cec5SDimitry Andric assert(CurrentFunctionDescription);
3905ffd83dbSDimitry Andric OS << "Null pointer passed as " << (Arg.ArgumentIndex + 1)
3915ffd83dbSDimitry Andric << llvm::getOrdinalSuffix(Arg.ArgumentIndex + 1) << " argument to "
392480093f4SDimitry Andric << CurrentFunctionDescription;
3930b57cec5SDimitry Andric
3945ffd83dbSDimitry Andric emitNullArgBug(C, stateNull, Arg.Expression, OS.str());
3950b57cec5SDimitry Andric }
3960b57cec5SDimitry Andric return nullptr;
3970b57cec5SDimitry Andric }
3980b57cec5SDimitry Andric
3990b57cec5SDimitry Andric // From here on, assume that the value is non-null.
4000b57cec5SDimitry Andric assert(stateNonNull);
4010b57cec5SDimitry Andric return stateNonNull;
4020b57cec5SDimitry Andric }
4030b57cec5SDimitry Andric
getIndex(ProgramStateRef State,const ElementRegion * ER,CharKind CK)404*0fca6ea1SDimitry Andric static std::optional<NonLoc> getIndex(ProgramStateRef State,
405*0fca6ea1SDimitry Andric const ElementRegion *ER, CharKind CK) {
406*0fca6ea1SDimitry Andric SValBuilder &SVB = State->getStateManager().getSValBuilder();
407*0fca6ea1SDimitry Andric ASTContext &Ctx = SVB.getContext();
408*0fca6ea1SDimitry Andric
409*0fca6ea1SDimitry Andric if (CK == CharKind::Regular) {
410*0fca6ea1SDimitry Andric if (ER->getValueType() != Ctx.CharTy)
411*0fca6ea1SDimitry Andric return {};
412*0fca6ea1SDimitry Andric return ER->getIndex();
413*0fca6ea1SDimitry Andric }
414*0fca6ea1SDimitry Andric
415*0fca6ea1SDimitry Andric if (ER->getValueType() != Ctx.WideCharTy)
416*0fca6ea1SDimitry Andric return {};
417*0fca6ea1SDimitry Andric
418*0fca6ea1SDimitry Andric QualType SizeTy = Ctx.getSizeType();
419*0fca6ea1SDimitry Andric NonLoc WideSize =
420*0fca6ea1SDimitry Andric SVB.makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),
421*0fca6ea1SDimitry Andric SizeTy)
422*0fca6ea1SDimitry Andric .castAs<NonLoc>();
423*0fca6ea1SDimitry Andric SVal Offset =
424*0fca6ea1SDimitry Andric SVB.evalBinOpNN(State, BO_Mul, ER->getIndex(), WideSize, SizeTy);
425*0fca6ea1SDimitry Andric if (Offset.isUnknown())
426*0fca6ea1SDimitry Andric return {};
427*0fca6ea1SDimitry Andric return Offset.castAs<NonLoc>();
428*0fca6ea1SDimitry Andric }
429*0fca6ea1SDimitry Andric
430*0fca6ea1SDimitry Andric // Basically 1 -> 1st, 12 -> 12th, etc.
printIdxWithOrdinalSuffix(llvm::raw_ostream & Os,unsigned Idx)431*0fca6ea1SDimitry Andric static void printIdxWithOrdinalSuffix(llvm::raw_ostream &Os, unsigned Idx) {
432*0fca6ea1SDimitry Andric Os << Idx << llvm::getOrdinalSuffix(Idx);
433*0fca6ea1SDimitry Andric }
434*0fca6ea1SDimitry Andric
checkInit(CheckerContext & C,ProgramStateRef State,AnyArgExpr Buffer,SVal Element,SVal Size) const435*0fca6ea1SDimitry Andric ProgramStateRef CStringChecker::checkInit(CheckerContext &C,
436*0fca6ea1SDimitry Andric ProgramStateRef State,
437*0fca6ea1SDimitry Andric AnyArgExpr Buffer, SVal Element,
438*0fca6ea1SDimitry Andric SVal Size) const {
439*0fca6ea1SDimitry Andric
440*0fca6ea1SDimitry Andric // If a previous check has failed, propagate the failure.
441*0fca6ea1SDimitry Andric if (!State)
442*0fca6ea1SDimitry Andric return nullptr;
443*0fca6ea1SDimitry Andric
444*0fca6ea1SDimitry Andric const MemRegion *R = Element.getAsRegion();
445*0fca6ea1SDimitry Andric const auto *ER = dyn_cast_or_null<ElementRegion>(R);
446*0fca6ea1SDimitry Andric if (!ER)
447*0fca6ea1SDimitry Andric return State;
448*0fca6ea1SDimitry Andric
449*0fca6ea1SDimitry Andric const auto *SuperR = ER->getSuperRegion()->getAs<TypedValueRegion>();
450*0fca6ea1SDimitry Andric if (!SuperR)
451*0fca6ea1SDimitry Andric return State;
452*0fca6ea1SDimitry Andric
453*0fca6ea1SDimitry Andric // FIXME: We ought to able to check objects as well. Maybe
454*0fca6ea1SDimitry Andric // UninitializedObjectChecker could help?
455*0fca6ea1SDimitry Andric if (!SuperR->getValueType()->isArrayType())
456*0fca6ea1SDimitry Andric return State;
457*0fca6ea1SDimitry Andric
458*0fca6ea1SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
459*0fca6ea1SDimitry Andric ASTContext &Ctx = SVB.getContext();
460*0fca6ea1SDimitry Andric
461*0fca6ea1SDimitry Andric const QualType ElemTy = Ctx.getBaseElementType(SuperR->getValueType());
462*0fca6ea1SDimitry Andric const NonLoc Zero = SVB.makeZeroArrayIndex();
463*0fca6ea1SDimitry Andric
464*0fca6ea1SDimitry Andric std::optional<Loc> FirstElementVal =
465*0fca6ea1SDimitry Andric State->getLValue(ElemTy, Zero, loc::MemRegionVal(SuperR)).getAs<Loc>();
466*0fca6ea1SDimitry Andric if (!FirstElementVal)
467*0fca6ea1SDimitry Andric return State;
468*0fca6ea1SDimitry Andric
469*0fca6ea1SDimitry Andric // Ensure that we wouldn't read uninitialized value.
470*0fca6ea1SDimitry Andric if (Filter.CheckCStringUninitializedRead &&
471*0fca6ea1SDimitry Andric State->getSVal(*FirstElementVal).isUndef()) {
472*0fca6ea1SDimitry Andric llvm::SmallString<258> Buf;
473*0fca6ea1SDimitry Andric llvm::raw_svector_ostream OS(Buf);
474*0fca6ea1SDimitry Andric OS << "The first element of the ";
475*0fca6ea1SDimitry Andric printIdxWithOrdinalSuffix(OS, Buffer.ArgumentIndex + 1);
476*0fca6ea1SDimitry Andric OS << " argument is undefined";
477*0fca6ea1SDimitry Andric emitUninitializedReadBug(C, State, Buffer.Expression, OS.str());
478*0fca6ea1SDimitry Andric return nullptr;
479*0fca6ea1SDimitry Andric }
480*0fca6ea1SDimitry Andric
481*0fca6ea1SDimitry Andric // We won't check whether the entire region is fully initialized -- lets just
482*0fca6ea1SDimitry Andric // check that the first and the last element is. So, onto checking the last
483*0fca6ea1SDimitry Andric // element:
484*0fca6ea1SDimitry Andric const QualType IdxTy = SVB.getArrayIndexType();
485*0fca6ea1SDimitry Andric
486*0fca6ea1SDimitry Andric NonLoc ElemSize =
487*0fca6ea1SDimitry Andric SVB.makeIntVal(Ctx.getTypeSizeInChars(ElemTy).getQuantity(), IdxTy)
488*0fca6ea1SDimitry Andric .castAs<NonLoc>();
489*0fca6ea1SDimitry Andric
490*0fca6ea1SDimitry Andric // FIXME: Check that the size arg to the cstring function is divisible by
491*0fca6ea1SDimitry Andric // size of the actual element type?
492*0fca6ea1SDimitry Andric
493*0fca6ea1SDimitry Andric // The type of the argument to the cstring function is either char or wchar,
494*0fca6ea1SDimitry Andric // but thats not the type of the original array (or memory region).
495*0fca6ea1SDimitry Andric // Suppose the following:
496*0fca6ea1SDimitry Andric // int t[5];
497*0fca6ea1SDimitry Andric // memcpy(dst, t, sizeof(t) / sizeof(t[0]));
498*0fca6ea1SDimitry Andric // When checking whether t is fully initialized, we see it as char array of
499*0fca6ea1SDimitry Andric // size sizeof(int)*5. If we check the last element as a character, we read
500*0fca6ea1SDimitry Andric // the last byte of an integer, which will be undefined. But just because
501*0fca6ea1SDimitry Andric // that value is undefined, it doesn't mean that the element is uninitialized!
502*0fca6ea1SDimitry Andric // For this reason, we need to retrieve the actual last element with the
503*0fca6ea1SDimitry Andric // correct type.
504*0fca6ea1SDimitry Andric
505*0fca6ea1SDimitry Andric // Divide the size argument to the cstring function by the actual element
506*0fca6ea1SDimitry Andric // type. This value will be size of the array, or the index to the
507*0fca6ea1SDimitry Andric // past-the-end element.
508*0fca6ea1SDimitry Andric std::optional<NonLoc> Offset =
509*0fca6ea1SDimitry Andric SVB.evalBinOpNN(State, clang::BO_Div, Size.castAs<NonLoc>(), ElemSize,
510*0fca6ea1SDimitry Andric IdxTy)
511*0fca6ea1SDimitry Andric .getAs<NonLoc>();
512*0fca6ea1SDimitry Andric
513*0fca6ea1SDimitry Andric // Retrieve the index of the last element.
514*0fca6ea1SDimitry Andric const NonLoc One = SVB.makeIntVal(1, IdxTy).castAs<NonLoc>();
515*0fca6ea1SDimitry Andric SVal LastIdx = SVB.evalBinOpNN(State, BO_Sub, *Offset, One, IdxTy);
516*0fca6ea1SDimitry Andric
517*0fca6ea1SDimitry Andric if (!Offset)
518*0fca6ea1SDimitry Andric return State;
519*0fca6ea1SDimitry Andric
520*0fca6ea1SDimitry Andric SVal LastElementVal =
521*0fca6ea1SDimitry Andric State->getLValue(ElemTy, LastIdx, loc::MemRegionVal(SuperR));
522*0fca6ea1SDimitry Andric if (!isa<Loc>(LastElementVal))
523*0fca6ea1SDimitry Andric return State;
524*0fca6ea1SDimitry Andric
525*0fca6ea1SDimitry Andric if (Filter.CheckCStringUninitializedRead &&
526*0fca6ea1SDimitry Andric State->getSVal(LastElementVal.castAs<Loc>()).isUndef()) {
527*0fca6ea1SDimitry Andric const llvm::APSInt *IdxInt = LastIdx.getAsInteger();
528*0fca6ea1SDimitry Andric // If we can't get emit a sensible last element index, just bail out --
529*0fca6ea1SDimitry Andric // prefer to emit nothing in favour of emitting garbage quality reports.
530*0fca6ea1SDimitry Andric if (!IdxInt) {
531*0fca6ea1SDimitry Andric C.addSink();
532*0fca6ea1SDimitry Andric return nullptr;
533*0fca6ea1SDimitry Andric }
534*0fca6ea1SDimitry Andric llvm::SmallString<258> Buf;
535*0fca6ea1SDimitry Andric llvm::raw_svector_ostream OS(Buf);
536*0fca6ea1SDimitry Andric OS << "The last accessed element (at index ";
537*0fca6ea1SDimitry Andric OS << IdxInt->getExtValue();
538*0fca6ea1SDimitry Andric OS << ") in the ";
539*0fca6ea1SDimitry Andric printIdxWithOrdinalSuffix(OS, Buffer.ArgumentIndex + 1);
540*0fca6ea1SDimitry Andric OS << " argument is undefined";
541*0fca6ea1SDimitry Andric emitUninitializedReadBug(C, State, Buffer.Expression, OS.str());
542*0fca6ea1SDimitry Andric return nullptr;
543*0fca6ea1SDimitry Andric }
544*0fca6ea1SDimitry Andric return State;
545*0fca6ea1SDimitry Andric }
546*0fca6ea1SDimitry Andric
5470b57cec5SDimitry Andric // FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
CheckLocation(CheckerContext & C,ProgramStateRef state,AnyArgExpr Buffer,SVal Element,AccessKind Access,CharKind CK) const5480b57cec5SDimitry Andric ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
5490b57cec5SDimitry Andric ProgramStateRef state,
5505ffd83dbSDimitry Andric AnyArgExpr Buffer, SVal Element,
551972a253aSDimitry Andric AccessKind Access,
552bdd1243dSDimitry Andric CharKind CK) const {
5535ffd83dbSDimitry Andric
5540b57cec5SDimitry Andric // If a previous check has failed, propagate the failure.
5550b57cec5SDimitry Andric if (!state)
5560b57cec5SDimitry Andric return nullptr;
5570b57cec5SDimitry Andric
5580b57cec5SDimitry Andric // Check for out of bound array element access.
5595ffd83dbSDimitry Andric const MemRegion *R = Element.getAsRegion();
5600b57cec5SDimitry Andric if (!R)
5610b57cec5SDimitry Andric return state;
5620b57cec5SDimitry Andric
5635ffd83dbSDimitry Andric const auto *ER = dyn_cast<ElementRegion>(R);
5640b57cec5SDimitry Andric if (!ER)
5650b57cec5SDimitry Andric return state;
5660b57cec5SDimitry Andric
567972a253aSDimitry Andric // Get the index of the accessed element.
568*0fca6ea1SDimitry Andric std::optional<NonLoc> Idx = getIndex(state, ER, CK);
569*0fca6ea1SDimitry Andric if (!Idx)
5700b57cec5SDimitry Andric return state;
5710b57cec5SDimitry Andric
5720b57cec5SDimitry Andric // Get the size of the array.
5735ffd83dbSDimitry Andric const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
5745ffd83dbSDimitry Andric DefinedOrUnknownSVal Size =
575fe6060f1SDimitry Andric getDynamicExtent(state, superReg, C.getSValBuilder());
5760b57cec5SDimitry Andric
577*0fca6ea1SDimitry Andric auto [StInBound, StOutBound] = state->assumeInBoundDual(*Idx, Size);
5780b57cec5SDimitry Andric if (StOutBound && !StInBound) {
5790b57cec5SDimitry Andric // These checks are either enabled by the CString out-of-bounds checker
5800b57cec5SDimitry Andric // explicitly or implicitly by the Malloc checker.
5810b57cec5SDimitry Andric // In the latter case we only do modeling but do not emit warning.
5820b57cec5SDimitry Andric if (!Filter.CheckCStringOutOfBounds)
5830b57cec5SDimitry Andric return nullptr;
5840b57cec5SDimitry Andric
5855ffd83dbSDimitry Andric // Emit a bug report.
5865ffd83dbSDimitry Andric ErrorMessage Message =
5875ffd83dbSDimitry Andric createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);
5885ffd83dbSDimitry Andric emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);
5890b57cec5SDimitry Andric return nullptr;
5900b57cec5SDimitry Andric }
5910b57cec5SDimitry Andric
5920b57cec5SDimitry Andric // Array bound check succeeded. From this point forward the array bound
5930b57cec5SDimitry Andric // should always succeed.
5940b57cec5SDimitry Andric return StInBound;
5950b57cec5SDimitry Andric }
5960b57cec5SDimitry Andric
597972a253aSDimitry Andric ProgramStateRef
CheckBufferAccess(CheckerContext & C,ProgramStateRef State,AnyArgExpr Buffer,SizeArgExpr Size,AccessKind Access,CharKind CK) const598972a253aSDimitry Andric CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
599972a253aSDimitry Andric AnyArgExpr Buffer, SizeArgExpr Size,
600bdd1243dSDimitry Andric AccessKind Access, CharKind CK) const {
6010b57cec5SDimitry Andric // If a previous check has failed, propagate the failure.
6025ffd83dbSDimitry Andric if (!State)
6030b57cec5SDimitry Andric return nullptr;
6040b57cec5SDimitry Andric
6050b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
6060b57cec5SDimitry Andric ASTContext &Ctx = svalBuilder.getContext();
6070b57cec5SDimitry Andric
6085ffd83dbSDimitry Andric QualType SizeTy = Size.Expression->getType();
609bdd1243dSDimitry Andric QualType PtrTy = getCharPtrType(Ctx, CK);
6100b57cec5SDimitry Andric
6110b57cec5SDimitry Andric // Check that the first buffer is non-null.
6125ffd83dbSDimitry Andric SVal BufVal = C.getSVal(Buffer.Expression);
6135ffd83dbSDimitry Andric State = checkNonNull(C, State, Buffer, BufVal);
6145ffd83dbSDimitry Andric if (!State)
6150b57cec5SDimitry Andric return nullptr;
6160b57cec5SDimitry Andric
6170b57cec5SDimitry Andric // If out-of-bounds checking is turned off, skip the rest.
6180b57cec5SDimitry Andric if (!Filter.CheckCStringOutOfBounds)
6195ffd83dbSDimitry Andric return State;
6200b57cec5SDimitry Andric
6215f757f3fSDimitry Andric SVal BufStart =
6225f757f3fSDimitry Andric svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());
6235f757f3fSDimitry Andric
6245f757f3fSDimitry Andric // Check if the first byte of the buffer is accessible.
6255f757f3fSDimitry Andric State = CheckLocation(C, State, Buffer, BufStart, Access, CK);
626*0fca6ea1SDimitry Andric
6275f757f3fSDimitry Andric if (!State)
6285f757f3fSDimitry Andric return nullptr;
6295f757f3fSDimitry Andric
6300b57cec5SDimitry Andric // Get the access length and make sure it is known.
6310b57cec5SDimitry Andric // FIXME: This assumes the caller has already checked that the access length
6320b57cec5SDimitry Andric // is positive. And that it's unsigned.
6335ffd83dbSDimitry Andric SVal LengthVal = C.getSVal(Size.Expression);
634bdd1243dSDimitry Andric std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
6350b57cec5SDimitry Andric if (!Length)
6365ffd83dbSDimitry Andric return State;
6370b57cec5SDimitry Andric
6380b57cec5SDimitry Andric // Compute the offset of the last element to be accessed: size-1.
6395ffd83dbSDimitry Andric NonLoc One = svalBuilder.makeIntVal(1, SizeTy).castAs<NonLoc>();
6405ffd83dbSDimitry Andric SVal Offset = svalBuilder.evalBinOpNN(State, BO_Sub, *Length, One, SizeTy);
6410b57cec5SDimitry Andric if (Offset.isUnknown())
6420b57cec5SDimitry Andric return nullptr;
6430b57cec5SDimitry Andric NonLoc LastOffset = Offset.castAs<NonLoc>();
6440b57cec5SDimitry Andric
6450b57cec5SDimitry Andric // Check that the first buffer is sufficiently long.
646bdd1243dSDimitry Andric if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
6470b57cec5SDimitry Andric
6485ffd83dbSDimitry Andric SVal BufEnd =
6495ffd83dbSDimitry Andric svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
650bdd1243dSDimitry Andric State = CheckLocation(C, State, Buffer, BufEnd, Access, CK);
651*0fca6ea1SDimitry Andric if (Access == AccessKind::read)
652*0fca6ea1SDimitry Andric State = checkInit(C, State, Buffer, BufEnd, *Length);
6530b57cec5SDimitry Andric
6540b57cec5SDimitry Andric // If the buffer isn't large enough, abort.
6555ffd83dbSDimitry Andric if (!State)
6560b57cec5SDimitry Andric return nullptr;
6570b57cec5SDimitry Andric }
6580b57cec5SDimitry Andric
6590b57cec5SDimitry Andric // Large enough or not, return this state!
6605ffd83dbSDimitry Andric return State;
6610b57cec5SDimitry Andric }
6620b57cec5SDimitry Andric
CheckOverlap(CheckerContext & C,ProgramStateRef state,SizeArgExpr Size,AnyArgExpr First,AnyArgExpr Second,CharKind CK) const6630b57cec5SDimitry Andric ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
6640b57cec5SDimitry Andric ProgramStateRef state,
6655ffd83dbSDimitry Andric SizeArgExpr Size, AnyArgExpr First,
666972a253aSDimitry Andric AnyArgExpr Second,
667bdd1243dSDimitry Andric CharKind CK) const {
6680b57cec5SDimitry Andric if (!Filter.CheckCStringBufferOverlap)
6690b57cec5SDimitry Andric return state;
6700b57cec5SDimitry Andric
6710b57cec5SDimitry Andric // Do a simple check for overlap: if the two arguments are from the same
6720b57cec5SDimitry Andric // buffer, see if the end of the first is greater than the start of the second
6730b57cec5SDimitry Andric // or vice versa.
6740b57cec5SDimitry Andric
6750b57cec5SDimitry Andric // If a previous check has failed, propagate the failure.
6760b57cec5SDimitry Andric if (!state)
6770b57cec5SDimitry Andric return nullptr;
6780b57cec5SDimitry Andric
6790b57cec5SDimitry Andric ProgramStateRef stateTrue, stateFalse;
6800b57cec5SDimitry Andric
68181ad6265SDimitry Andric // Assume different address spaces cannot overlap.
68281ad6265SDimitry Andric if (First.Expression->getType()->getPointeeType().getAddressSpace() !=
68381ad6265SDimitry Andric Second.Expression->getType()->getPointeeType().getAddressSpace())
68481ad6265SDimitry Andric return state;
68581ad6265SDimitry Andric
6860b57cec5SDimitry Andric // Get the buffer values and make sure they're known locations.
6870b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
6885ffd83dbSDimitry Andric SVal firstVal = state->getSVal(First.Expression, LCtx);
6895ffd83dbSDimitry Andric SVal secondVal = state->getSVal(Second.Expression, LCtx);
6900b57cec5SDimitry Andric
691bdd1243dSDimitry Andric std::optional<Loc> firstLoc = firstVal.getAs<Loc>();
6920b57cec5SDimitry Andric if (!firstLoc)
6930b57cec5SDimitry Andric return state;
6940b57cec5SDimitry Andric
695bdd1243dSDimitry Andric std::optional<Loc> secondLoc = secondVal.getAs<Loc>();
6960b57cec5SDimitry Andric if (!secondLoc)
6970b57cec5SDimitry Andric return state;
6980b57cec5SDimitry Andric
6990b57cec5SDimitry Andric // Are the two values the same?
7000b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
7010b57cec5SDimitry Andric std::tie(stateTrue, stateFalse) =
7020b57cec5SDimitry Andric state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
7030b57cec5SDimitry Andric
7040b57cec5SDimitry Andric if (stateTrue && !stateFalse) {
7050b57cec5SDimitry Andric // If the values are known to be equal, that's automatically an overlap.
7065ffd83dbSDimitry Andric emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
7070b57cec5SDimitry Andric return nullptr;
7080b57cec5SDimitry Andric }
7090b57cec5SDimitry Andric
7100b57cec5SDimitry Andric // assume the two expressions are not equal.
7110b57cec5SDimitry Andric assert(stateFalse);
7120b57cec5SDimitry Andric state = stateFalse;
7130b57cec5SDimitry Andric
7140b57cec5SDimitry Andric // Which value comes first?
7150b57cec5SDimitry Andric QualType cmpTy = svalBuilder.getConditionType();
7165ffd83dbSDimitry Andric SVal reverse =
7175ffd83dbSDimitry Andric svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);
718bdd1243dSDimitry Andric std::optional<DefinedOrUnknownSVal> reverseTest =
7190b57cec5SDimitry Andric reverse.getAs<DefinedOrUnknownSVal>();
7200b57cec5SDimitry Andric if (!reverseTest)
7210b57cec5SDimitry Andric return state;
7220b57cec5SDimitry Andric
7230b57cec5SDimitry Andric std::tie(stateTrue, stateFalse) = state->assume(*reverseTest);
7240b57cec5SDimitry Andric if (stateTrue) {
7250b57cec5SDimitry Andric if (stateFalse) {
7260b57cec5SDimitry Andric // If we don't know which one comes first, we can't perform this test.
7270b57cec5SDimitry Andric return state;
7280b57cec5SDimitry Andric } else {
7290b57cec5SDimitry Andric // Switch the values so that firstVal is before secondVal.
7300b57cec5SDimitry Andric std::swap(firstLoc, secondLoc);
7310b57cec5SDimitry Andric
7320b57cec5SDimitry Andric // Switch the Exprs as well, so that they still correspond.
7330b57cec5SDimitry Andric std::swap(First, Second);
7340b57cec5SDimitry Andric }
7350b57cec5SDimitry Andric }
7360b57cec5SDimitry Andric
7370b57cec5SDimitry Andric // Get the length, and make sure it too is known.
7385ffd83dbSDimitry Andric SVal LengthVal = state->getSVal(Size.Expression, LCtx);
739bdd1243dSDimitry Andric std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
7400b57cec5SDimitry Andric if (!Length)
7410b57cec5SDimitry Andric return state;
7420b57cec5SDimitry Andric
7430b57cec5SDimitry Andric // Convert the first buffer's start address to char*.
7440b57cec5SDimitry Andric // Bail out if the cast fails.
7450b57cec5SDimitry Andric ASTContext &Ctx = svalBuilder.getContext();
746bdd1243dSDimitry Andric QualType CharPtrTy = getCharPtrType(Ctx, CK);
7475ffd83dbSDimitry Andric SVal FirstStart =
7485ffd83dbSDimitry Andric svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
749bdd1243dSDimitry Andric std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
7500b57cec5SDimitry Andric if (!FirstStartLoc)
7510b57cec5SDimitry Andric return state;
7520b57cec5SDimitry Andric
7530b57cec5SDimitry Andric // Compute the end of the first buffer. Bail out if THAT fails.
7545ffd83dbSDimitry Andric SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,
7555ffd83dbSDimitry Andric *Length, CharPtrTy);
756bdd1243dSDimitry Andric std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
7570b57cec5SDimitry Andric if (!FirstEndLoc)
7580b57cec5SDimitry Andric return state;
7590b57cec5SDimitry Andric
7600b57cec5SDimitry Andric // Is the end of the first buffer past the start of the second buffer?
7615ffd83dbSDimitry Andric SVal Overlap =
7625ffd83dbSDimitry Andric svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);
763bdd1243dSDimitry Andric std::optional<DefinedOrUnknownSVal> OverlapTest =
7640b57cec5SDimitry Andric Overlap.getAs<DefinedOrUnknownSVal>();
7650b57cec5SDimitry Andric if (!OverlapTest)
7660b57cec5SDimitry Andric return state;
7670b57cec5SDimitry Andric
7680b57cec5SDimitry Andric std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);
7690b57cec5SDimitry Andric
7700b57cec5SDimitry Andric if (stateTrue && !stateFalse) {
7710b57cec5SDimitry Andric // Overlap!
7725ffd83dbSDimitry Andric emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);
7730b57cec5SDimitry Andric return nullptr;
7740b57cec5SDimitry Andric }
7750b57cec5SDimitry Andric
7760b57cec5SDimitry Andric // assume the two expressions don't overlap.
7770b57cec5SDimitry Andric assert(stateFalse);
7780b57cec5SDimitry Andric return stateFalse;
7790b57cec5SDimitry Andric }
7800b57cec5SDimitry Andric
emitOverlapBug(CheckerContext & C,ProgramStateRef state,const Stmt * First,const Stmt * Second) const7810b57cec5SDimitry Andric void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
7820b57cec5SDimitry Andric const Stmt *First, const Stmt *Second) const {
7830b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(state);
7840b57cec5SDimitry Andric if (!N)
7850b57cec5SDimitry Andric return;
7860b57cec5SDimitry Andric
7870b57cec5SDimitry Andric if (!BT_Overlap)
7880b57cec5SDimitry Andric BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
7890b57cec5SDimitry Andric categories::UnixAPI, "Improper arguments"));
7900b57cec5SDimitry Andric
7910b57cec5SDimitry Andric // Generate a report for this bug.
792a7dea167SDimitry Andric auto report = std::make_unique<PathSensitiveBugReport>(
7930b57cec5SDimitry Andric *BT_Overlap, "Arguments must not be overlapping buffers", N);
7940b57cec5SDimitry Andric report->addRange(First->getSourceRange());
7950b57cec5SDimitry Andric report->addRange(Second->getSourceRange());
7960b57cec5SDimitry Andric
7970b57cec5SDimitry Andric C.emitReport(std::move(report));
7980b57cec5SDimitry Andric }
7990b57cec5SDimitry Andric
emitNullArgBug(CheckerContext & C,ProgramStateRef State,const Stmt * S,StringRef WarningMsg) const8000b57cec5SDimitry Andric void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
8010b57cec5SDimitry Andric const Stmt *S, StringRef WarningMsg) const {
8020b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) {
8035f757f3fSDimitry Andric if (!BT_Null) {
8045f757f3fSDimitry Andric // FIXME: This call uses the string constant 'categories::UnixAPI' as the
8055f757f3fSDimitry Andric // description of the bug; it should be replaced by a real description.
8065f757f3fSDimitry Andric BT_Null.reset(
8075f757f3fSDimitry Andric new BugType(Filter.CheckNameCStringNullArg, categories::UnixAPI));
8085f757f3fSDimitry Andric }
8090b57cec5SDimitry Andric
8105f757f3fSDimitry Andric auto Report =
8115f757f3fSDimitry Andric std::make_unique<PathSensitiveBugReport>(*BT_Null, WarningMsg, N);
8120b57cec5SDimitry Andric Report->addRange(S->getSourceRange());
8130b57cec5SDimitry Andric if (const auto *Ex = dyn_cast<Expr>(S))
8140b57cec5SDimitry Andric bugreporter::trackExpressionValue(N, Ex, *Report);
8150b57cec5SDimitry Andric C.emitReport(std::move(Report));
8160b57cec5SDimitry Andric }
8170b57cec5SDimitry Andric }
8180b57cec5SDimitry Andric
emitUninitializedReadBug(CheckerContext & C,ProgramStateRef State,const Expr * E,StringRef Msg) const81981ad6265SDimitry Andric void CStringChecker::emitUninitializedReadBug(CheckerContext &C,
82081ad6265SDimitry Andric ProgramStateRef State,
821*0fca6ea1SDimitry Andric const Expr *E,
822*0fca6ea1SDimitry Andric StringRef Msg) const {
82381ad6265SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) {
82481ad6265SDimitry Andric if (!BT_UninitRead)
8255f757f3fSDimitry Andric BT_UninitRead.reset(new BugType(Filter.CheckNameCStringUninitializedRead,
8265f757f3fSDimitry Andric "Accessing unitialized/garbage values"));
82781ad6265SDimitry Andric
8285f757f3fSDimitry Andric auto Report =
8295f757f3fSDimitry Andric std::make_unique<PathSensitiveBugReport>(*BT_UninitRead, Msg, N);
830*0fca6ea1SDimitry Andric Report->addNote("Other elements might also be undefined",
831*0fca6ea1SDimitry Andric Report->getLocation());
83281ad6265SDimitry Andric Report->addRange(E->getSourceRange());
83381ad6265SDimitry Andric bugreporter::trackExpressionValue(N, E, *Report);
83481ad6265SDimitry Andric C.emitReport(std::move(Report));
83581ad6265SDimitry Andric }
83681ad6265SDimitry Andric }
83781ad6265SDimitry Andric
emitOutOfBoundsBug(CheckerContext & C,ProgramStateRef State,const Stmt * S,StringRef WarningMsg) const8380b57cec5SDimitry Andric void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
8390b57cec5SDimitry Andric ProgramStateRef State, const Stmt *S,
8400b57cec5SDimitry Andric StringRef WarningMsg) const {
8410b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) {
8420b57cec5SDimitry Andric if (!BT_Bounds)
8435f757f3fSDimitry Andric BT_Bounds.reset(new BugType(Filter.CheckCStringOutOfBounds
8445f757f3fSDimitry Andric ? Filter.CheckNameCStringOutOfBounds
8450b57cec5SDimitry Andric : Filter.CheckNameCStringNullArg,
8465f757f3fSDimitry Andric "Out-of-bound array access"));
8470b57cec5SDimitry Andric
8480b57cec5SDimitry Andric // FIXME: It would be nice to eventually make this diagnostic more clear,
8490b57cec5SDimitry Andric // e.g., by referencing the original declaration or by saying *why* this
8500b57cec5SDimitry Andric // reference is outside the range.
8515f757f3fSDimitry Andric auto Report =
8525f757f3fSDimitry Andric std::make_unique<PathSensitiveBugReport>(*BT_Bounds, WarningMsg, N);
8530b57cec5SDimitry Andric Report->addRange(S->getSourceRange());
8540b57cec5SDimitry Andric C.emitReport(std::move(Report));
8550b57cec5SDimitry Andric }
8560b57cec5SDimitry Andric }
8570b57cec5SDimitry Andric
emitNotCStringBug(CheckerContext & C,ProgramStateRef State,const Stmt * S,StringRef WarningMsg) const8580b57cec5SDimitry Andric void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
8590b57cec5SDimitry Andric const Stmt *S,
8600b57cec5SDimitry Andric StringRef WarningMsg) const {
8610b57cec5SDimitry Andric if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
8625f757f3fSDimitry Andric if (!BT_NotCString) {
8635f757f3fSDimitry Andric // FIXME: This call uses the string constant 'categories::UnixAPI' as the
8645f757f3fSDimitry Andric // description of the bug; it should be replaced by a real description.
8655f757f3fSDimitry Andric BT_NotCString.reset(
8665f757f3fSDimitry Andric new BugType(Filter.CheckNameCStringNotNullTerm, categories::UnixAPI));
8675f757f3fSDimitry Andric }
8680b57cec5SDimitry Andric
869a7dea167SDimitry Andric auto Report =
870a7dea167SDimitry Andric std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N);
8710b57cec5SDimitry Andric
8720b57cec5SDimitry Andric Report->addRange(S->getSourceRange());
8730b57cec5SDimitry Andric C.emitReport(std::move(Report));
8740b57cec5SDimitry Andric }
8750b57cec5SDimitry Andric }
8760b57cec5SDimitry Andric
emitAdditionOverflowBug(CheckerContext & C,ProgramStateRef State) const8770b57cec5SDimitry Andric void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
8780b57cec5SDimitry Andric ProgramStateRef State) const {
8790b57cec5SDimitry Andric if (ExplodedNode *N = C.generateErrorNode(State)) {
8805f757f3fSDimitry Andric if (!BT_AdditionOverflow) {
8815f757f3fSDimitry Andric // FIXME: This call uses the word "API" as the description of the bug;
8825f757f3fSDimitry Andric // it should be replaced by a better error message (if this unlikely
8835f757f3fSDimitry Andric // situation continues to exist as a separate bug type).
88481ad6265SDimitry Andric BT_AdditionOverflow.reset(
8855f757f3fSDimitry Andric new BugType(Filter.CheckNameCStringOutOfBounds, "API"));
8865f757f3fSDimitry Andric }
8870b57cec5SDimitry Andric
8880b57cec5SDimitry Andric // This isn't a great error message, but this should never occur in real
8890b57cec5SDimitry Andric // code anyway -- you'd have to create a buffer longer than a size_t can
8900b57cec5SDimitry Andric // represent, which is sort of a contradiction.
8910b57cec5SDimitry Andric const char *WarningMsg =
8920b57cec5SDimitry Andric "This expression will create a string whose length is too big to "
8930b57cec5SDimitry Andric "be represented as a size_t";
8940b57cec5SDimitry Andric
89581ad6265SDimitry Andric auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow,
89681ad6265SDimitry Andric WarningMsg, N);
8970b57cec5SDimitry Andric C.emitReport(std::move(Report));
8980b57cec5SDimitry Andric }
8990b57cec5SDimitry Andric }
9000b57cec5SDimitry Andric
checkAdditionOverflow(CheckerContext & C,ProgramStateRef state,NonLoc left,NonLoc right) const9010b57cec5SDimitry Andric ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
9020b57cec5SDimitry Andric ProgramStateRef state,
9030b57cec5SDimitry Andric NonLoc left,
9040b57cec5SDimitry Andric NonLoc right) const {
9050b57cec5SDimitry Andric // If out-of-bounds checking is turned off, skip the rest.
9060b57cec5SDimitry Andric if (!Filter.CheckCStringOutOfBounds)
9070b57cec5SDimitry Andric return state;
9080b57cec5SDimitry Andric
9090b57cec5SDimitry Andric // If a previous check has failed, propagate the failure.
9100b57cec5SDimitry Andric if (!state)
9110b57cec5SDimitry Andric return nullptr;
9120b57cec5SDimitry Andric
9130b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
9140b57cec5SDimitry Andric BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
9150b57cec5SDimitry Andric
9160b57cec5SDimitry Andric QualType sizeTy = svalBuilder.getContext().getSizeType();
9170b57cec5SDimitry Andric const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
9180b57cec5SDimitry Andric NonLoc maxVal = svalBuilder.makeIntVal(maxValInt);
9190b57cec5SDimitry Andric
9200b57cec5SDimitry Andric SVal maxMinusRight;
92181ad6265SDimitry Andric if (isa<nonloc::ConcreteInt>(right)) {
9220b57cec5SDimitry Andric maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right,
9230b57cec5SDimitry Andric sizeTy);
9240b57cec5SDimitry Andric } else {
9250b57cec5SDimitry Andric // Try switching the operands. (The order of these two assignments is
9260b57cec5SDimitry Andric // important!)
9270b57cec5SDimitry Andric maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,
9280b57cec5SDimitry Andric sizeTy);
9290b57cec5SDimitry Andric left = right;
9300b57cec5SDimitry Andric }
9310b57cec5SDimitry Andric
932bdd1243dSDimitry Andric if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
9330b57cec5SDimitry Andric QualType cmpTy = svalBuilder.getConditionType();
9340b57cec5SDimitry Andric // If left > max - right, we have an overflow.
9350b57cec5SDimitry Andric SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
9360b57cec5SDimitry Andric *maxMinusRightNL, cmpTy);
9370b57cec5SDimitry Andric
9380b57cec5SDimitry Andric ProgramStateRef stateOverflow, stateOkay;
9390b57cec5SDimitry Andric std::tie(stateOverflow, stateOkay) =
9400b57cec5SDimitry Andric state->assume(willOverflow.castAs<DefinedOrUnknownSVal>());
9410b57cec5SDimitry Andric
9420b57cec5SDimitry Andric if (stateOverflow && !stateOkay) {
9430b57cec5SDimitry Andric // We have an overflow. Emit a bug report.
9440b57cec5SDimitry Andric emitAdditionOverflowBug(C, stateOverflow);
9450b57cec5SDimitry Andric return nullptr;
9460b57cec5SDimitry Andric }
9470b57cec5SDimitry Andric
9480b57cec5SDimitry Andric // From now on, assume an overflow didn't occur.
9490b57cec5SDimitry Andric assert(stateOkay);
9500b57cec5SDimitry Andric state = stateOkay;
9510b57cec5SDimitry Andric }
9520b57cec5SDimitry Andric
9530b57cec5SDimitry Andric return state;
9540b57cec5SDimitry Andric }
9550b57cec5SDimitry Andric
setCStringLength(ProgramStateRef state,const MemRegion * MR,SVal strLength)9560b57cec5SDimitry Andric ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
9570b57cec5SDimitry Andric const MemRegion *MR,
9580b57cec5SDimitry Andric SVal strLength) {
9590b57cec5SDimitry Andric assert(!strLength.isUndef() && "Attempt to set an undefined string length");
9600b57cec5SDimitry Andric
9610b57cec5SDimitry Andric MR = MR->StripCasts();
9620b57cec5SDimitry Andric
9630b57cec5SDimitry Andric switch (MR->getKind()) {
9640b57cec5SDimitry Andric case MemRegion::StringRegionKind:
9650b57cec5SDimitry Andric // FIXME: This can happen if we strcpy() into a string region. This is
9660b57cec5SDimitry Andric // undefined [C99 6.4.5p6], but we should still warn about it.
9670b57cec5SDimitry Andric return state;
9680b57cec5SDimitry Andric
9690b57cec5SDimitry Andric case MemRegion::SymbolicRegionKind:
9700b57cec5SDimitry Andric case MemRegion::AllocaRegionKind:
9715ffd83dbSDimitry Andric case MemRegion::NonParamVarRegionKind:
9725ffd83dbSDimitry Andric case MemRegion::ParamVarRegionKind:
9730b57cec5SDimitry Andric case MemRegion::FieldRegionKind:
9740b57cec5SDimitry Andric case MemRegion::ObjCIvarRegionKind:
9750b57cec5SDimitry Andric // These are the types we can currently track string lengths for.
9760b57cec5SDimitry Andric break;
9770b57cec5SDimitry Andric
9780b57cec5SDimitry Andric case MemRegion::ElementRegionKind:
9790b57cec5SDimitry Andric // FIXME: Handle element regions by upper-bounding the parent region's
9800b57cec5SDimitry Andric // string length.
9810b57cec5SDimitry Andric return state;
9820b57cec5SDimitry Andric
9830b57cec5SDimitry Andric default:
9840b57cec5SDimitry Andric // Other regions (mostly non-data) can't have a reliable C string length.
9850b57cec5SDimitry Andric // For now, just ignore the change.
9860b57cec5SDimitry Andric // FIXME: These are rare but not impossible. We should output some kind of
9870b57cec5SDimitry Andric // warning for things like strcpy((char[]){'a', 0}, "b");
9880b57cec5SDimitry Andric return state;
9890b57cec5SDimitry Andric }
9900b57cec5SDimitry Andric
9910b57cec5SDimitry Andric if (strLength.isUnknown())
9920b57cec5SDimitry Andric return state->remove<CStringLength>(MR);
9930b57cec5SDimitry Andric
9940b57cec5SDimitry Andric return state->set<CStringLength>(MR, strLength);
9950b57cec5SDimitry Andric }
9960b57cec5SDimitry Andric
getCStringLengthForRegion(CheckerContext & C,ProgramStateRef & state,const Expr * Ex,const MemRegion * MR,bool hypothetical)9970b57cec5SDimitry Andric SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
9980b57cec5SDimitry Andric ProgramStateRef &state,
9990b57cec5SDimitry Andric const Expr *Ex,
10000b57cec5SDimitry Andric const MemRegion *MR,
10010b57cec5SDimitry Andric bool hypothetical) {
10020b57cec5SDimitry Andric if (!hypothetical) {
10030b57cec5SDimitry Andric // If there's a recorded length, go ahead and return it.
10040b57cec5SDimitry Andric const SVal *Recorded = state->get<CStringLength>(MR);
10050b57cec5SDimitry Andric if (Recorded)
10060b57cec5SDimitry Andric return *Recorded;
10070b57cec5SDimitry Andric }
10080b57cec5SDimitry Andric
10090b57cec5SDimitry Andric // Otherwise, get a new symbol and update the state.
10100b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
10110b57cec5SDimitry Andric QualType sizeTy = svalBuilder.getContext().getSizeType();
10120b57cec5SDimitry Andric SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(),
10130b57cec5SDimitry Andric MR, Ex, sizeTy,
10140b57cec5SDimitry Andric C.getLocationContext(),
10150b57cec5SDimitry Andric C.blockCount());
10160b57cec5SDimitry Andric
10170b57cec5SDimitry Andric if (!hypothetical) {
1018bdd1243dSDimitry Andric if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
10190b57cec5SDimitry Andric // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4
10200b57cec5SDimitry Andric BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
10210b57cec5SDimitry Andric const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
10220b57cec5SDimitry Andric llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4);
10230b57cec5SDimitry Andric const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt,
10240b57cec5SDimitry Andric fourInt);
10250b57cec5SDimitry Andric NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt);
10265f757f3fSDimitry Andric SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, maxLength,
10275f757f3fSDimitry Andric svalBuilder.getConditionType());
10280b57cec5SDimitry Andric state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true);
10290b57cec5SDimitry Andric }
10300b57cec5SDimitry Andric state = state->set<CStringLength>(MR, strLength);
10310b57cec5SDimitry Andric }
10320b57cec5SDimitry Andric
10330b57cec5SDimitry Andric return strLength;
10340b57cec5SDimitry Andric }
10350b57cec5SDimitry Andric
getCStringLength(CheckerContext & C,ProgramStateRef & state,const Expr * Ex,SVal Buf,bool hypothetical) const10360b57cec5SDimitry Andric SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
10370b57cec5SDimitry Andric const Expr *Ex, SVal Buf,
10380b57cec5SDimitry Andric bool hypothetical) const {
10390b57cec5SDimitry Andric const MemRegion *MR = Buf.getAsRegion();
10400b57cec5SDimitry Andric if (!MR) {
10410b57cec5SDimitry Andric // If we can't get a region, see if it's something we /know/ isn't a
10420b57cec5SDimitry Andric // C string. In the context of locations, the only time we can issue such
10430b57cec5SDimitry Andric // a warning is for labels.
1044bdd1243dSDimitry Andric if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
10450b57cec5SDimitry Andric if (Filter.CheckCStringNotNullTerm) {
10460b57cec5SDimitry Andric SmallString<120> buf;
10470b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf);
10480b57cec5SDimitry Andric assert(CurrentFunctionDescription);
10490b57cec5SDimitry Andric os << "Argument to " << CurrentFunctionDescription
10500b57cec5SDimitry Andric << " is the address of the label '" << Label->getLabel()->getName()
10510b57cec5SDimitry Andric << "', which is not a null-terminated string";
10520b57cec5SDimitry Andric
10530b57cec5SDimitry Andric emitNotCStringBug(C, state, Ex, os.str());
10540b57cec5SDimitry Andric }
10550b57cec5SDimitry Andric return UndefinedVal();
10560b57cec5SDimitry Andric }
10570b57cec5SDimitry Andric
10580b57cec5SDimitry Andric // If it's not a region and not a label, give up.
10590b57cec5SDimitry Andric return UnknownVal();
10600b57cec5SDimitry Andric }
10610b57cec5SDimitry Andric
10620b57cec5SDimitry Andric // If we have a region, strip casts from it and see if we can figure out
10630b57cec5SDimitry Andric // its length. For anything we can't figure out, just return UnknownVal.
10640b57cec5SDimitry Andric MR = MR->StripCasts();
10650b57cec5SDimitry Andric
10660b57cec5SDimitry Andric switch (MR->getKind()) {
10670b57cec5SDimitry Andric case MemRegion::StringRegionKind: {
10680b57cec5SDimitry Andric // Modifying the contents of string regions is undefined [C99 6.4.5p6],
10690b57cec5SDimitry Andric // so we can assume that the byte length is the correct C string length.
10700b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
10710b57cec5SDimitry Andric QualType sizeTy = svalBuilder.getContext().getSizeType();
10720b57cec5SDimitry Andric const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
1073753f127fSDimitry Andric return svalBuilder.makeIntVal(strLit->getLength(), sizeTy);
10740b57cec5SDimitry Andric }
10755f757f3fSDimitry Andric case MemRegion::NonParamVarRegionKind: {
10765f757f3fSDimitry Andric // If we have a global constant with a string literal initializer,
10775f757f3fSDimitry Andric // compute the initializer's length.
10785f757f3fSDimitry Andric const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl();
10795f757f3fSDimitry Andric if (Decl->getType().isConstQualified() && Decl->hasGlobalStorage()) {
10805f757f3fSDimitry Andric if (const Expr *Init = Decl->getInit()) {
10815f757f3fSDimitry Andric if (auto *StrLit = dyn_cast<StringLiteral>(Init)) {
10825f757f3fSDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder();
10835f757f3fSDimitry Andric QualType SizeTy = SvalBuilder.getContext().getSizeType();
10845f757f3fSDimitry Andric return SvalBuilder.makeIntVal(StrLit->getLength(), SizeTy);
10855f757f3fSDimitry Andric }
10865f757f3fSDimitry Andric }
10875f757f3fSDimitry Andric }
10885f757f3fSDimitry Andric [[fallthrough]];
10895f757f3fSDimitry Andric }
10900b57cec5SDimitry Andric case MemRegion::SymbolicRegionKind:
10910b57cec5SDimitry Andric case MemRegion::AllocaRegionKind:
10925ffd83dbSDimitry Andric case MemRegion::ParamVarRegionKind:
10930b57cec5SDimitry Andric case MemRegion::FieldRegionKind:
10940b57cec5SDimitry Andric case MemRegion::ObjCIvarRegionKind:
10950b57cec5SDimitry Andric return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
10960b57cec5SDimitry Andric case MemRegion::CompoundLiteralRegionKind:
10970b57cec5SDimitry Andric // FIXME: Can we track this? Is it necessary?
10980b57cec5SDimitry Andric return UnknownVal();
10990b57cec5SDimitry Andric case MemRegion::ElementRegionKind:
11000b57cec5SDimitry Andric // FIXME: How can we handle this? It's not good enough to subtract the
11010b57cec5SDimitry Andric // offset from the base string length; consider "123\x00567" and &a[5].
11020b57cec5SDimitry Andric return UnknownVal();
11030b57cec5SDimitry Andric default:
11040b57cec5SDimitry Andric // Other regions (mostly non-data) can't have a reliable C string length.
11050b57cec5SDimitry Andric // In this case, an error is emitted and UndefinedVal is returned.
11060b57cec5SDimitry Andric // The caller should always be prepared to handle this case.
11070b57cec5SDimitry Andric if (Filter.CheckCStringNotNullTerm) {
11080b57cec5SDimitry Andric SmallString<120> buf;
11090b57cec5SDimitry Andric llvm::raw_svector_ostream os(buf);
11100b57cec5SDimitry Andric
11110b57cec5SDimitry Andric assert(CurrentFunctionDescription);
11120b57cec5SDimitry Andric os << "Argument to " << CurrentFunctionDescription << " is ";
11130b57cec5SDimitry Andric
11140b57cec5SDimitry Andric if (SummarizeRegion(os, C.getASTContext(), MR))
11150b57cec5SDimitry Andric os << ", which is not a null-terminated string";
11160b57cec5SDimitry Andric else
11170b57cec5SDimitry Andric os << "not a null-terminated string";
11180b57cec5SDimitry Andric
11190b57cec5SDimitry Andric emitNotCStringBug(C, state, Ex, os.str());
11200b57cec5SDimitry Andric }
11210b57cec5SDimitry Andric return UndefinedVal();
11220b57cec5SDimitry Andric }
11230b57cec5SDimitry Andric }
11240b57cec5SDimitry Andric
getCStringLiteral(CheckerContext & C,ProgramStateRef & state,const Expr * expr,SVal val) const11250b57cec5SDimitry Andric const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
11260b57cec5SDimitry Andric ProgramStateRef &state, const Expr *expr, SVal val) const {
11270b57cec5SDimitry Andric
11280b57cec5SDimitry Andric // Get the memory region pointed to by the val.
11290b57cec5SDimitry Andric const MemRegion *bufRegion = val.getAsRegion();
11300b57cec5SDimitry Andric if (!bufRegion)
11310b57cec5SDimitry Andric return nullptr;
11320b57cec5SDimitry Andric
11330b57cec5SDimitry Andric // Strip casts off the memory region.
11340b57cec5SDimitry Andric bufRegion = bufRegion->StripCasts();
11350b57cec5SDimitry Andric
11360b57cec5SDimitry Andric // Cast the memory region to a string region.
11370b57cec5SDimitry Andric const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);
11380b57cec5SDimitry Andric if (!strRegion)
11390b57cec5SDimitry Andric return nullptr;
11400b57cec5SDimitry Andric
11410b57cec5SDimitry Andric // Return the actual string in the string region.
11420b57cec5SDimitry Andric return strRegion->getStringLiteral();
11430b57cec5SDimitry Andric }
11440b57cec5SDimitry Andric
isFirstBufInBound(CheckerContext & C,ProgramStateRef State,SVal BufVal,QualType BufTy,SVal LengthVal,QualType LengthTy)114506c3fb27SDimitry Andric bool CStringChecker::isFirstBufInBound(CheckerContext &C, ProgramStateRef State,
114606c3fb27SDimitry Andric SVal BufVal, QualType BufTy,
114706c3fb27SDimitry Andric SVal LengthVal, QualType LengthTy) {
11480b57cec5SDimitry Andric // If we do not know that the buffer is long enough we return 'true'.
11490b57cec5SDimitry Andric // Otherwise the parent region of this field region would also get
11500b57cec5SDimitry Andric // invalidated, which would lead to warnings based on an unknown state.
11510b57cec5SDimitry Andric
115206c3fb27SDimitry Andric if (LengthVal.isUnknown())
115306c3fb27SDimitry Andric return false;
115406c3fb27SDimitry Andric
11550b57cec5SDimitry Andric // Originally copied from CheckBufferAccess and CheckLocation.
115606c3fb27SDimitry Andric SValBuilder &SB = C.getSValBuilder();
115706c3fb27SDimitry Andric ASTContext &Ctx = C.getASTContext();
11580b57cec5SDimitry Andric
11590b57cec5SDimitry Andric QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
11600b57cec5SDimitry Andric
1161bdd1243dSDimitry Andric std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
11620b57cec5SDimitry Andric if (!Length)
11630b57cec5SDimitry Andric return true; // cf top comment.
11640b57cec5SDimitry Andric
11650b57cec5SDimitry Andric // Compute the offset of the last element to be accessed: size-1.
116606c3fb27SDimitry Andric NonLoc One = SB.makeIntVal(1, LengthTy).castAs<NonLoc>();
116706c3fb27SDimitry Andric SVal Offset = SB.evalBinOpNN(State, BO_Sub, *Length, One, LengthTy);
11680b57cec5SDimitry Andric if (Offset.isUnknown())
11690b57cec5SDimitry Andric return true; // cf top comment
11700b57cec5SDimitry Andric NonLoc LastOffset = Offset.castAs<NonLoc>();
11710b57cec5SDimitry Andric
11720b57cec5SDimitry Andric // Check that the first buffer is sufficiently long.
117306c3fb27SDimitry Andric SVal BufStart = SB.evalCast(BufVal, PtrTy, BufTy);
1174bdd1243dSDimitry Andric std::optional<Loc> BufLoc = BufStart.getAs<Loc>();
11750b57cec5SDimitry Andric if (!BufLoc)
11760b57cec5SDimitry Andric return true; // cf top comment.
11770b57cec5SDimitry Andric
117806c3fb27SDimitry Andric SVal BufEnd = SB.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
11790b57cec5SDimitry Andric
11800b57cec5SDimitry Andric // Check for out of bound array element access.
11810b57cec5SDimitry Andric const MemRegion *R = BufEnd.getAsRegion();
11820b57cec5SDimitry Andric if (!R)
11830b57cec5SDimitry Andric return true; // cf top comment.
11840b57cec5SDimitry Andric
11850b57cec5SDimitry Andric const ElementRegion *ER = dyn_cast<ElementRegion>(R);
11860b57cec5SDimitry Andric if (!ER)
11870b57cec5SDimitry Andric return true; // cf top comment.
11880b57cec5SDimitry Andric
11890b57cec5SDimitry Andric // FIXME: Does this crash when a non-standard definition
11900b57cec5SDimitry Andric // of a library function is encountered?
11910b57cec5SDimitry Andric assert(ER->getValueType() == C.getASTContext().CharTy &&
119206c3fb27SDimitry Andric "isFirstBufInBound should only be called with char* ElementRegions");
11930b57cec5SDimitry Andric
11940b57cec5SDimitry Andric // Get the size of the array.
11950b57cec5SDimitry Andric const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
119606c3fb27SDimitry Andric DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, superReg, SB);
11970b57cec5SDimitry Andric
11980b57cec5SDimitry Andric // Get the index of the accessed element.
11990b57cec5SDimitry Andric DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
12000b57cec5SDimitry Andric
120106c3fb27SDimitry Andric ProgramStateRef StInBound = State->assumeInBound(Idx, SizeDV, true);
12020b57cec5SDimitry Andric
12030b57cec5SDimitry Andric return static_cast<bool>(StInBound);
12040b57cec5SDimitry Andric }
12050b57cec5SDimitry Andric
invalidateDestinationBufferBySize(CheckerContext & C,ProgramStateRef S,const Expr * BufE,SVal BufV,SVal SizeV,QualType SizeTy)120606c3fb27SDimitry Andric ProgramStateRef CStringChecker::invalidateDestinationBufferBySize(
120706c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV,
120806c3fb27SDimitry Andric SVal SizeV, QualType SizeTy) {
120906c3fb27SDimitry Andric auto InvalidationTraitOperations =
121006c3fb27SDimitry Andric [&C, S, BufTy = BufE->getType(), BufV, SizeV,
121106c3fb27SDimitry Andric SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
121206c3fb27SDimitry Andric // If destination buffer is a field region and access is in bound, do
121306c3fb27SDimitry Andric // not invalidate its super region.
121406c3fb27SDimitry Andric if (MemRegion::FieldRegionKind == R->getKind() &&
121506c3fb27SDimitry Andric isFirstBufInBound(C, S, BufV, BufTy, SizeV, SizeTy)) {
121606c3fb27SDimitry Andric ITraits.setTrait(
121706c3fb27SDimitry Andric R,
121806c3fb27SDimitry Andric RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
121906c3fb27SDimitry Andric }
122006c3fb27SDimitry Andric return false;
122106c3fb27SDimitry Andric };
122206c3fb27SDimitry Andric
122306c3fb27SDimitry Andric return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
122406c3fb27SDimitry Andric }
122506c3fb27SDimitry Andric
122606c3fb27SDimitry Andric ProgramStateRef
invalidateDestinationBufferAlwaysEscapeSuperRegion(CheckerContext & C,ProgramStateRef S,const Expr * BufE,SVal BufV)122706c3fb27SDimitry Andric CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion(
122806c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
122906c3fb27SDimitry Andric auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &,
123006c3fb27SDimitry Andric const MemRegion *R) {
123106c3fb27SDimitry Andric return isa<FieldRegion>(R);
123206c3fb27SDimitry Andric };
123306c3fb27SDimitry Andric
123406c3fb27SDimitry Andric return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
123506c3fb27SDimitry Andric }
123606c3fb27SDimitry Andric
invalidateDestinationBufferNeverOverflows(CheckerContext & C,ProgramStateRef S,const Expr * BufE,SVal BufV)123706c3fb27SDimitry Andric ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows(
123806c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef S, const Expr *BufE, SVal BufV) {
123906c3fb27SDimitry Andric auto InvalidationTraitOperations =
124006c3fb27SDimitry Andric [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
124106c3fb27SDimitry Andric if (MemRegion::FieldRegionKind == R->getKind())
124206c3fb27SDimitry Andric ITraits.setTrait(
124306c3fb27SDimitry Andric R,
124406c3fb27SDimitry Andric RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
124506c3fb27SDimitry Andric return false;
124606c3fb27SDimitry Andric };
124706c3fb27SDimitry Andric
124806c3fb27SDimitry Andric return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
124906c3fb27SDimitry Andric }
125006c3fb27SDimitry Andric
invalidateSourceBuffer(CheckerContext & C,ProgramStateRef S,const Expr * BufE,SVal BufV)125106c3fb27SDimitry Andric ProgramStateRef CStringChecker::invalidateSourceBuffer(CheckerContext &C,
125206c3fb27SDimitry Andric ProgramStateRef S,
125306c3fb27SDimitry Andric const Expr *BufE,
125406c3fb27SDimitry Andric SVal BufV) {
125506c3fb27SDimitry Andric auto InvalidationTraitOperations =
125606c3fb27SDimitry Andric [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {
125706c3fb27SDimitry Andric ITraits.setTrait(
125806c3fb27SDimitry Andric R->getBaseRegion(),
125906c3fb27SDimitry Andric RegionAndSymbolInvalidationTraits::TK_PreserveContents);
126006c3fb27SDimitry Andric ITraits.setTrait(R,
126106c3fb27SDimitry Andric RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
126206c3fb27SDimitry Andric return true;
126306c3fb27SDimitry Andric };
126406c3fb27SDimitry Andric
126506c3fb27SDimitry Andric return invalidateBufferAux(C, S, BufE, BufV, InvalidationTraitOperations);
126606c3fb27SDimitry Andric }
126706c3fb27SDimitry Andric
invalidateBufferAux(CheckerContext & C,ProgramStateRef State,const Expr * E,SVal V,llvm::function_ref<bool (RegionAndSymbolInvalidationTraits &,const MemRegion *)> InvalidationTraitOperations)126806c3fb27SDimitry Andric ProgramStateRef CStringChecker::invalidateBufferAux(
126906c3fb27SDimitry Andric CheckerContext &C, ProgramStateRef State, const Expr *E, SVal V,
127006c3fb27SDimitry Andric llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,
127106c3fb27SDimitry Andric const MemRegion *)>
127206c3fb27SDimitry Andric InvalidationTraitOperations) {
1273bdd1243dSDimitry Andric std::optional<Loc> L = V.getAs<Loc>();
12740b57cec5SDimitry Andric if (!L)
127506c3fb27SDimitry Andric return State;
12760b57cec5SDimitry Andric
12770b57cec5SDimitry Andric // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
12780b57cec5SDimitry Andric // some assumptions about the value that CFRefCount can't. Even so, it should
12790b57cec5SDimitry Andric // probably be refactored.
1280bdd1243dSDimitry Andric if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
12810b57cec5SDimitry Andric const MemRegion *R = MR->getRegion()->StripCasts();
12820b57cec5SDimitry Andric
12830b57cec5SDimitry Andric // Are we dealing with an ElementRegion? If so, we should be invalidating
12840b57cec5SDimitry Andric // the super-region.
12850b57cec5SDimitry Andric if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
12860b57cec5SDimitry Andric R = ER->getSuperRegion();
12870b57cec5SDimitry Andric // FIXME: What about layers of ElementRegions?
12880b57cec5SDimitry Andric }
12890b57cec5SDimitry Andric
12900b57cec5SDimitry Andric // Invalidate this region.
12910b57cec5SDimitry Andric const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
12920b57cec5SDimitry Andric RegionAndSymbolInvalidationTraits ITraits;
129306c3fb27SDimitry Andric bool CausesPointerEscape = InvalidationTraitOperations(ITraits, R);
12940b57cec5SDimitry Andric
129506c3fb27SDimitry Andric return State->invalidateRegions(R, E, C.blockCount(), LCtx,
12960b57cec5SDimitry Andric CausesPointerEscape, nullptr, nullptr,
12970b57cec5SDimitry Andric &ITraits);
12980b57cec5SDimitry Andric }
12990b57cec5SDimitry Andric
13000b57cec5SDimitry Andric // If we have a non-region value by chance, just remove the binding.
13010b57cec5SDimitry Andric // FIXME: is this necessary or correct? This handles the non-Region
13020b57cec5SDimitry Andric // cases. Is it ever valid to store to these?
130306c3fb27SDimitry Andric return State->killBinding(*L);
13040b57cec5SDimitry Andric }
13050b57cec5SDimitry Andric
SummarizeRegion(raw_ostream & os,ASTContext & Ctx,const MemRegion * MR)13060b57cec5SDimitry Andric bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
13070b57cec5SDimitry Andric const MemRegion *MR) {
13080b57cec5SDimitry Andric switch (MR->getKind()) {
13090b57cec5SDimitry Andric case MemRegion::FunctionCodeRegionKind: {
1310480093f4SDimitry Andric if (const auto *FD = cast<FunctionCodeRegion>(MR)->getDecl())
13110b57cec5SDimitry Andric os << "the address of the function '" << *FD << '\'';
13120b57cec5SDimitry Andric else
13130b57cec5SDimitry Andric os << "the address of a function";
13140b57cec5SDimitry Andric return true;
13150b57cec5SDimitry Andric }
13160b57cec5SDimitry Andric case MemRegion::BlockCodeRegionKind:
13170b57cec5SDimitry Andric os << "block text";
13180b57cec5SDimitry Andric return true;
13190b57cec5SDimitry Andric case MemRegion::BlockDataRegionKind:
13200b57cec5SDimitry Andric os << "a block";
13210b57cec5SDimitry Andric return true;
13220b57cec5SDimitry Andric case MemRegion::CXXThisRegionKind:
13230b57cec5SDimitry Andric case MemRegion::CXXTempObjectRegionKind:
1324480093f4SDimitry Andric os << "a C++ temp object of type "
132581ad6265SDimitry Andric << cast<TypedValueRegion>(MR)->getValueType();
13260b57cec5SDimitry Andric return true;
13275ffd83dbSDimitry Andric case MemRegion::NonParamVarRegionKind:
132881ad6265SDimitry Andric os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType();
13290b57cec5SDimitry Andric return true;
13305ffd83dbSDimitry Andric case MemRegion::ParamVarRegionKind:
133181ad6265SDimitry Andric os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType();
13325ffd83dbSDimitry Andric return true;
13330b57cec5SDimitry Andric case MemRegion::FieldRegionKind:
133481ad6265SDimitry Andric os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType();
13350b57cec5SDimitry Andric return true;
13360b57cec5SDimitry Andric case MemRegion::ObjCIvarRegionKind:
1337480093f4SDimitry Andric os << "an instance variable of type "
133881ad6265SDimitry Andric << cast<TypedValueRegion>(MR)->getValueType();
13390b57cec5SDimitry Andric return true;
13400b57cec5SDimitry Andric default:
13410b57cec5SDimitry Andric return false;
13420b57cec5SDimitry Andric }
13430b57cec5SDimitry Andric }
13440b57cec5SDimitry Andric
memsetAux(const Expr * DstBuffer,SVal CharVal,const Expr * Size,CheckerContext & C,ProgramStateRef & State)13450b57cec5SDimitry Andric bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
13460b57cec5SDimitry Andric const Expr *Size, CheckerContext &C,
13470b57cec5SDimitry Andric ProgramStateRef &State) {
13480b57cec5SDimitry Andric SVal MemVal = C.getSVal(DstBuffer);
13490b57cec5SDimitry Andric SVal SizeVal = C.getSVal(Size);
13500b57cec5SDimitry Andric const MemRegion *MR = MemVal.getAsRegion();
13510b57cec5SDimitry Andric if (!MR)
13520b57cec5SDimitry Andric return false;
13530b57cec5SDimitry Andric
13540b57cec5SDimitry Andric // We're about to model memset by producing a "default binding" in the Store.
13550b57cec5SDimitry Andric // Our current implementation - RegionStore - doesn't support default bindings
13560b57cec5SDimitry Andric // that don't cover the whole base region. So we should first get the offset
13570b57cec5SDimitry Andric // and the base region to figure out whether the offset of buffer is 0.
13580b57cec5SDimitry Andric RegionOffset Offset = MR->getAsOffset();
13590b57cec5SDimitry Andric const MemRegion *BR = Offset.getRegion();
13600b57cec5SDimitry Andric
1361bdd1243dSDimitry Andric std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
13620b57cec5SDimitry Andric if (!SizeNL)
13630b57cec5SDimitry Andric return false;
13640b57cec5SDimitry Andric
13650b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
13660b57cec5SDimitry Andric ASTContext &Ctx = C.getASTContext();
13670b57cec5SDimitry Andric
13680b57cec5SDimitry Andric // void *memset(void *dest, int ch, size_t count);
13690b57cec5SDimitry Andric // For now we can only handle the case of offset is 0 and concrete char value.
13700b57cec5SDimitry Andric if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
13710b57cec5SDimitry Andric Offset.getOffset() == 0) {
13725ffd83dbSDimitry Andric // Get the base region's size.
1373fe6060f1SDimitry Andric DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, BR, svalBuilder);
13740b57cec5SDimitry Andric
13750b57cec5SDimitry Andric ProgramStateRef StateWholeReg, StateNotWholeReg;
13760b57cec5SDimitry Andric std::tie(StateWholeReg, StateNotWholeReg) =
13775ffd83dbSDimitry Andric State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL));
13780b57cec5SDimitry Andric
13790b57cec5SDimitry Andric // With the semantic of 'memset()', we should convert the CharVal to
13800b57cec5SDimitry Andric // unsigned char.
13810b57cec5SDimitry Andric CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);
13820b57cec5SDimitry Andric
13830b57cec5SDimitry Andric ProgramStateRef StateNullChar, StateNonNullChar;
13840b57cec5SDimitry Andric std::tie(StateNullChar, StateNonNullChar) =
13850b57cec5SDimitry Andric assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
13860b57cec5SDimitry Andric
13870b57cec5SDimitry Andric if (StateWholeReg && !StateNotWholeReg && StateNullChar &&
13880b57cec5SDimitry Andric !StateNonNullChar) {
13890b57cec5SDimitry Andric // If the 'memset()' acts on the whole region of destination buffer and
13900b57cec5SDimitry Andric // the value of the second argument of 'memset()' is zero, bind the second
13910b57cec5SDimitry Andric // argument's value to the destination buffer with 'default binding'.
13920b57cec5SDimitry Andric // FIXME: Since there is no perfect way to bind the non-zero character, we
13930b57cec5SDimitry Andric // can only deal with zero value here. In the future, we need to deal with
13940b57cec5SDimitry Andric // the binding of non-zero value in the case of whole region.
13950b57cec5SDimitry Andric State = State->bindDefaultZero(svalBuilder.makeLoc(BR),
13960b57cec5SDimitry Andric C.getLocationContext());
13970b57cec5SDimitry Andric } else {
13980b57cec5SDimitry Andric // If the destination buffer's extent is not equal to the value of
13990b57cec5SDimitry Andric // third argument, just invalidate buffer.
140006c3fb27SDimitry Andric State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal,
140106c3fb27SDimitry Andric SizeVal, Size->getType());
14020b57cec5SDimitry Andric }
14030b57cec5SDimitry Andric
14040b57cec5SDimitry Andric if (StateNullChar && !StateNonNullChar) {
14050b57cec5SDimitry Andric // If the value of the second argument of 'memset()' is zero, set the
14060b57cec5SDimitry Andric // string length of destination buffer to 0 directly.
14070b57cec5SDimitry Andric State = setCStringLength(State, MR,
14080b57cec5SDimitry Andric svalBuilder.makeZeroVal(Ctx.getSizeType()));
14090b57cec5SDimitry Andric } else if (!StateNullChar && StateNonNullChar) {
14100b57cec5SDimitry Andric SVal NewStrLen = svalBuilder.getMetadataSymbolVal(
14110b57cec5SDimitry Andric CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(),
14120b57cec5SDimitry Andric C.getLocationContext(), C.blockCount());
14130b57cec5SDimitry Andric
14140b57cec5SDimitry Andric // If the value of second argument is not zero, then the string length
14150b57cec5SDimitry Andric // is at least the size argument.
14160b57cec5SDimitry Andric SVal NewStrLenGESize = svalBuilder.evalBinOp(
14170b57cec5SDimitry Andric State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType());
14180b57cec5SDimitry Andric
14190b57cec5SDimitry Andric State = setCStringLength(
14200b57cec5SDimitry Andric State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true),
14210b57cec5SDimitry Andric MR, NewStrLen);
14220b57cec5SDimitry Andric }
14230b57cec5SDimitry Andric } else {
14240b57cec5SDimitry Andric // If the offset is not zero and char value is not concrete, we can do
14250b57cec5SDimitry Andric // nothing but invalidate the buffer.
142606c3fb27SDimitry Andric State = invalidateDestinationBufferBySize(C, State, DstBuffer, MemVal,
142706c3fb27SDimitry Andric SizeVal, Size->getType());
14280b57cec5SDimitry Andric }
14290b57cec5SDimitry Andric return true;
14300b57cec5SDimitry Andric }
14310b57cec5SDimitry Andric
14320b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
14330b57cec5SDimitry Andric // evaluation of individual function calls.
14340b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
14350b57cec5SDimitry Andric
evalCopyCommon(CheckerContext & C,const CallEvent & Call,ProgramStateRef state,SizeArgExpr Size,DestinationArgExpr Dest,SourceArgExpr Source,bool Restricted,bool IsMempcpy,CharKind CK) const1436647cbc5dSDimitry Andric void CStringChecker::evalCopyCommon(CheckerContext &C, const CallEvent &Call,
14375ffd83dbSDimitry Andric ProgramStateRef state, SizeArgExpr Size,
14385ffd83dbSDimitry Andric DestinationArgExpr Dest,
14395ffd83dbSDimitry Andric SourceArgExpr Source, bool Restricted,
1440bdd1243dSDimitry Andric bool IsMempcpy, CharKind CK) const {
14410b57cec5SDimitry Andric CurrentFunctionDescription = "memory copy function";
14420b57cec5SDimitry Andric
14430b57cec5SDimitry Andric // See if the size argument is zero.
14440b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
14455ffd83dbSDimitry Andric SVal sizeVal = state->getSVal(Size.Expression, LCtx);
14465ffd83dbSDimitry Andric QualType sizeTy = Size.Expression->getType();
14470b57cec5SDimitry Andric
14480b57cec5SDimitry Andric ProgramStateRef stateZeroSize, stateNonZeroSize;
14490b57cec5SDimitry Andric std::tie(stateZeroSize, stateNonZeroSize) =
14500b57cec5SDimitry Andric assumeZero(C, state, sizeVal, sizeTy);
14510b57cec5SDimitry Andric
14520b57cec5SDimitry Andric // Get the value of the Dest.
14535ffd83dbSDimitry Andric SVal destVal = state->getSVal(Dest.Expression, LCtx);
14540b57cec5SDimitry Andric
14550b57cec5SDimitry Andric // If the size is zero, there won't be any actual memory access, so
14560b57cec5SDimitry Andric // just bind the return value to the destination buffer and return.
14570b57cec5SDimitry Andric if (stateZeroSize && !stateNonZeroSize) {
1458647cbc5dSDimitry Andric stateZeroSize =
1459647cbc5dSDimitry Andric stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, destVal);
14600b57cec5SDimitry Andric C.addTransition(stateZeroSize);
14610b57cec5SDimitry Andric return;
14620b57cec5SDimitry Andric }
14630b57cec5SDimitry Andric
14640b57cec5SDimitry Andric // If the size can be nonzero, we have to check the other arguments.
14650b57cec5SDimitry Andric if (stateNonZeroSize) {
1466*0fca6ea1SDimitry Andric // TODO: If Size is tainted and we cannot prove that it is smaller or equal
1467*0fca6ea1SDimitry Andric // to the size of the destination buffer, then emit a warning
1468*0fca6ea1SDimitry Andric // that an attacker may provoke a buffer overflow error.
14690b57cec5SDimitry Andric state = stateNonZeroSize;
14700b57cec5SDimitry Andric
14710b57cec5SDimitry Andric // Ensure the destination is not null. If it is NULL there will be a
14720b57cec5SDimitry Andric // NULL pointer dereference.
14735ffd83dbSDimitry Andric state = checkNonNull(C, state, Dest, destVal);
14740b57cec5SDimitry Andric if (!state)
14750b57cec5SDimitry Andric return;
14760b57cec5SDimitry Andric
14770b57cec5SDimitry Andric // Get the value of the Src.
14785ffd83dbSDimitry Andric SVal srcVal = state->getSVal(Source.Expression, LCtx);
14790b57cec5SDimitry Andric
14800b57cec5SDimitry Andric // Ensure the source is not null. If it is NULL there will be a
14810b57cec5SDimitry Andric // NULL pointer dereference.
14825ffd83dbSDimitry Andric state = checkNonNull(C, state, Source, srcVal);
14830b57cec5SDimitry Andric if (!state)
14840b57cec5SDimitry Andric return;
14850b57cec5SDimitry Andric
14860b57cec5SDimitry Andric // Ensure the accesses are valid and that the buffers do not overlap.
1487bdd1243dSDimitry Andric state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK);
1488bdd1243dSDimitry Andric state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK);
14895ffd83dbSDimitry Andric
14900b57cec5SDimitry Andric if (Restricted)
1491bdd1243dSDimitry Andric state = CheckOverlap(C, state, Size, Dest, Source, CK);
14920b57cec5SDimitry Andric
14930b57cec5SDimitry Andric if (!state)
14940b57cec5SDimitry Andric return;
14950b57cec5SDimitry Andric
14960b57cec5SDimitry Andric // If this is mempcpy, get the byte after the last byte copied and
14970b57cec5SDimitry Andric // bind the expr.
14980b57cec5SDimitry Andric if (IsMempcpy) {
14990b57cec5SDimitry Andric // Get the byte after the last byte copied.
15000b57cec5SDimitry Andric SValBuilder &SvalBuilder = C.getSValBuilder();
15010b57cec5SDimitry Andric ASTContext &Ctx = SvalBuilder.getContext();
1502bdd1243dSDimitry Andric QualType CharPtrTy = getCharPtrType(Ctx, CK);
15030b57cec5SDimitry Andric SVal DestRegCharVal =
15045ffd83dbSDimitry Andric SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());
15050b57cec5SDimitry Andric SVal lastElement = C.getSValBuilder().evalBinOp(
15065ffd83dbSDimitry Andric state, BO_Add, DestRegCharVal, sizeVal, Dest.Expression->getType());
15070b57cec5SDimitry Andric // If we don't know how much we copied, we can at least
15080b57cec5SDimitry Andric // conjure a return value for later.
15090b57cec5SDimitry Andric if (lastElement.isUnknown())
1510647cbc5dSDimitry Andric lastElement = C.getSValBuilder().conjureSymbolVal(
1511647cbc5dSDimitry Andric nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
15120b57cec5SDimitry Andric
15130b57cec5SDimitry Andric // The byte after the last byte copied is the return value.
1514647cbc5dSDimitry Andric state = state->BindExpr(Call.getOriginExpr(), LCtx, lastElement);
15150b57cec5SDimitry Andric } else {
15160b57cec5SDimitry Andric // All other copies return the destination buffer.
15170b57cec5SDimitry Andric // (Well, bcopy() has a void return type, but this won't hurt.)
1518647cbc5dSDimitry Andric state = state->BindExpr(Call.getOriginExpr(), LCtx, destVal);
15190b57cec5SDimitry Andric }
15200b57cec5SDimitry Andric
15210b57cec5SDimitry Andric // Invalidate the destination (regular invalidation without pointer-escaping
15220b57cec5SDimitry Andric // the address of the top-level region).
15230b57cec5SDimitry Andric // FIXME: Even if we can't perfectly model the copy, we should see if we
15240b57cec5SDimitry Andric // can use LazyCompoundVals to copy the source values into the destination.
15250b57cec5SDimitry Andric // This would probably remove any existing bindings past the end of the
15260b57cec5SDimitry Andric // copied region, but that's still an improvement over blank invalidation.
152706c3fb27SDimitry Andric state = invalidateDestinationBufferBySize(
152806c3fb27SDimitry Andric C, state, Dest.Expression, C.getSVal(Dest.Expression), sizeVal,
152906c3fb27SDimitry Andric Size.Expression->getType());
15300b57cec5SDimitry Andric
15310b57cec5SDimitry Andric // Invalidate the source (const-invalidation without const-pointer-escaping
15320b57cec5SDimitry Andric // the address of the top-level region).
153306c3fb27SDimitry Andric state = invalidateSourceBuffer(C, state, Source.Expression,
153406c3fb27SDimitry Andric C.getSVal(Source.Expression));
15350b57cec5SDimitry Andric
15360b57cec5SDimitry Andric C.addTransition(state);
15370b57cec5SDimitry Andric }
15380b57cec5SDimitry Andric }
15390b57cec5SDimitry Andric
evalMemcpy(CheckerContext & C,const CallEvent & Call,CharKind CK) const1540647cbc5dSDimitry Andric void CStringChecker::evalMemcpy(CheckerContext &C, const CallEvent &Call,
1541bdd1243dSDimitry Andric CharKind CK) const {
15420b57cec5SDimitry Andric // void *memcpy(void *restrict dst, const void *restrict src, size_t n);
15430b57cec5SDimitry Andric // The return value is the address of the destination buffer.
1544647cbc5dSDimitry Andric DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
1545647cbc5dSDimitry Andric SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
1546647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
15470b57cec5SDimitry Andric
15485ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
15495ffd83dbSDimitry Andric
15505ffd83dbSDimitry Andric constexpr bool IsRestricted = true;
15515ffd83dbSDimitry Andric constexpr bool IsMempcpy = false;
1552647cbc5dSDimitry Andric evalCopyCommon(C, Call, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK);
15530b57cec5SDimitry Andric }
15540b57cec5SDimitry Andric
evalMempcpy(CheckerContext & C,const CallEvent & Call,CharKind CK) const1555647cbc5dSDimitry Andric void CStringChecker::evalMempcpy(CheckerContext &C, const CallEvent &Call,
1556bdd1243dSDimitry Andric CharKind CK) const {
15570b57cec5SDimitry Andric // void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
15580b57cec5SDimitry Andric // The return value is a pointer to the byte following the last written byte.
1559647cbc5dSDimitry Andric DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
1560647cbc5dSDimitry Andric SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
1561647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
15620b57cec5SDimitry Andric
15635ffd83dbSDimitry Andric constexpr bool IsRestricted = true;
15645ffd83dbSDimitry Andric constexpr bool IsMempcpy = true;
1565647cbc5dSDimitry Andric evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
1566647cbc5dSDimitry Andric IsMempcpy, CK);
15670b57cec5SDimitry Andric }
15680b57cec5SDimitry Andric
evalMemmove(CheckerContext & C,const CallEvent & Call,CharKind CK) const1569647cbc5dSDimitry Andric void CStringChecker::evalMemmove(CheckerContext &C, const CallEvent &Call,
1570bdd1243dSDimitry Andric CharKind CK) const {
15710b57cec5SDimitry Andric // void *memmove(void *dst, const void *src, size_t n);
15720b57cec5SDimitry Andric // The return value is the address of the destination buffer.
1573647cbc5dSDimitry Andric DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
1574647cbc5dSDimitry Andric SourceArgExpr Src = {{Call.getArgExpr(1), 1}};
1575647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
15760b57cec5SDimitry Andric
15775ffd83dbSDimitry Andric constexpr bool IsRestricted = false;
15785ffd83dbSDimitry Andric constexpr bool IsMempcpy = false;
1579647cbc5dSDimitry Andric evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
1580647cbc5dSDimitry Andric IsMempcpy, CK);
15810b57cec5SDimitry Andric }
15820b57cec5SDimitry Andric
evalBcopy(CheckerContext & C,const CallEvent & Call) const1583647cbc5dSDimitry Andric void CStringChecker::evalBcopy(CheckerContext &C, const CallEvent &Call) const {
15840b57cec5SDimitry Andric // void bcopy(const void *src, void *dst, size_t n);
1585647cbc5dSDimitry Andric SourceArgExpr Src{{Call.getArgExpr(0), 0}};
1586647cbc5dSDimitry Andric DestinationArgExpr Dest = {{Call.getArgExpr(1), 1}};
1587647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
15885ffd83dbSDimitry Andric
15895ffd83dbSDimitry Andric constexpr bool IsRestricted = false;
15905ffd83dbSDimitry Andric constexpr bool IsMempcpy = false;
1591647cbc5dSDimitry Andric evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,
1592647cbc5dSDimitry Andric IsMempcpy, CharKind::Regular);
15930b57cec5SDimitry Andric }
15940b57cec5SDimitry Andric
evalMemcmp(CheckerContext & C,const CallEvent & Call,CharKind CK) const1595647cbc5dSDimitry Andric void CStringChecker::evalMemcmp(CheckerContext &C, const CallEvent &Call,
1596bdd1243dSDimitry Andric CharKind CK) const {
15970b57cec5SDimitry Andric // int memcmp(const void *s1, const void *s2, size_t n);
15980b57cec5SDimitry Andric CurrentFunctionDescription = "memory comparison function";
15990b57cec5SDimitry Andric
1600647cbc5dSDimitry Andric AnyArgExpr Left = {Call.getArgExpr(0), 0};
1601647cbc5dSDimitry Andric AnyArgExpr Right = {Call.getArgExpr(1), 1};
1602647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
16030b57cec5SDimitry Andric
16045ffd83dbSDimitry Andric ProgramStateRef State = C.getState();
16055ffd83dbSDimitry Andric SValBuilder &Builder = C.getSValBuilder();
16065ffd83dbSDimitry Andric const LocationContext *LCtx = C.getLocationContext();
16070b57cec5SDimitry Andric
16080b57cec5SDimitry Andric // See if the size argument is zero.
16095ffd83dbSDimitry Andric SVal sizeVal = State->getSVal(Size.Expression, LCtx);
16105ffd83dbSDimitry Andric QualType sizeTy = Size.Expression->getType();
16110b57cec5SDimitry Andric
16120b57cec5SDimitry Andric ProgramStateRef stateZeroSize, stateNonZeroSize;
16130b57cec5SDimitry Andric std::tie(stateZeroSize, stateNonZeroSize) =
16145ffd83dbSDimitry Andric assumeZero(C, State, sizeVal, sizeTy);
16150b57cec5SDimitry Andric
16160b57cec5SDimitry Andric // If the size can be zero, the result will be 0 in that case, and we don't
16170b57cec5SDimitry Andric // have to check either of the buffers.
16180b57cec5SDimitry Andric if (stateZeroSize) {
16195ffd83dbSDimitry Andric State = stateZeroSize;
1620647cbc5dSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), LCtx,
1621647cbc5dSDimitry Andric Builder.makeZeroVal(Call.getResultType()));
16225ffd83dbSDimitry Andric C.addTransition(State);
16230b57cec5SDimitry Andric }
16240b57cec5SDimitry Andric
16250b57cec5SDimitry Andric // If the size can be nonzero, we have to check the other arguments.
16260b57cec5SDimitry Andric if (stateNonZeroSize) {
16275ffd83dbSDimitry Andric State = stateNonZeroSize;
16280b57cec5SDimitry Andric // If we know the two buffers are the same, we know the result is 0.
16290b57cec5SDimitry Andric // First, get the two buffers' addresses. Another checker will have already
16300b57cec5SDimitry Andric // made sure they're not undefined.
16310b57cec5SDimitry Andric DefinedOrUnknownSVal LV =
16325ffd83dbSDimitry Andric State->getSVal(Left.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
16330b57cec5SDimitry Andric DefinedOrUnknownSVal RV =
16345ffd83dbSDimitry Andric State->getSVal(Right.Expression, LCtx).castAs<DefinedOrUnknownSVal>();
16350b57cec5SDimitry Andric
16360b57cec5SDimitry Andric // See if they are the same.
16375ffd83dbSDimitry Andric ProgramStateRef SameBuffer, NotSameBuffer;
16385ffd83dbSDimitry Andric std::tie(SameBuffer, NotSameBuffer) =
16395ffd83dbSDimitry Andric State->assume(Builder.evalEQ(State, LV, RV));
16400b57cec5SDimitry Andric
1641480093f4SDimitry Andric // If the two arguments are the same buffer, we know the result is 0,
16420b57cec5SDimitry Andric // and we only need to check one size.
16435ffd83dbSDimitry Andric if (SameBuffer && !NotSameBuffer) {
16445ffd83dbSDimitry Andric State = SameBuffer;
16455ffd83dbSDimitry Andric State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);
16465ffd83dbSDimitry Andric if (State) {
1647647cbc5dSDimitry Andric State = SameBuffer->BindExpr(Call.getOriginExpr(), LCtx,
1648647cbc5dSDimitry Andric Builder.makeZeroVal(Call.getResultType()));
16495ffd83dbSDimitry Andric C.addTransition(State);
16500b57cec5SDimitry Andric }
1651480093f4SDimitry Andric return;
16520b57cec5SDimitry Andric }
16530b57cec5SDimitry Andric
1654480093f4SDimitry Andric // If the two arguments might be different buffers, we have to check
1655480093f4SDimitry Andric // the size of both of them.
16565ffd83dbSDimitry Andric assert(NotSameBuffer);
1657bdd1243dSDimitry Andric State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK);
1658bdd1243dSDimitry Andric State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK);
16595ffd83dbSDimitry Andric if (State) {
16600b57cec5SDimitry Andric // The return value is the comparison result, which we don't know.
1661647cbc5dSDimitry Andric SVal CmpV = Builder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
1662647cbc5dSDimitry Andric C.blockCount());
1663647cbc5dSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), LCtx, CmpV);
16645ffd83dbSDimitry Andric C.addTransition(State);
16650b57cec5SDimitry Andric }
16660b57cec5SDimitry Andric }
16670b57cec5SDimitry Andric }
16680b57cec5SDimitry Andric
evalstrLength(CheckerContext & C,const CallEvent & Call) const16690b57cec5SDimitry Andric void CStringChecker::evalstrLength(CheckerContext &C,
1670647cbc5dSDimitry Andric const CallEvent &Call) const {
16710b57cec5SDimitry Andric // size_t strlen(const char *s);
1672647cbc5dSDimitry Andric evalstrLengthCommon(C, Call, /* IsStrnlen = */ false);
16730b57cec5SDimitry Andric }
16740b57cec5SDimitry Andric
evalstrnLength(CheckerContext & C,const CallEvent & Call) const16750b57cec5SDimitry Andric void CStringChecker::evalstrnLength(CheckerContext &C,
1676647cbc5dSDimitry Andric const CallEvent &Call) const {
16770b57cec5SDimitry Andric // size_t strnlen(const char *s, size_t maxlen);
1678647cbc5dSDimitry Andric evalstrLengthCommon(C, Call, /* IsStrnlen = */ true);
16790b57cec5SDimitry Andric }
16800b57cec5SDimitry Andric
evalstrLengthCommon(CheckerContext & C,const CallEvent & Call,bool IsStrnlen) const1681647cbc5dSDimitry Andric void CStringChecker::evalstrLengthCommon(CheckerContext &C,
1682647cbc5dSDimitry Andric const CallEvent &Call,
16830b57cec5SDimitry Andric bool IsStrnlen) const {
16840b57cec5SDimitry Andric CurrentFunctionDescription = "string length function";
16850b57cec5SDimitry Andric ProgramStateRef state = C.getState();
16860b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
16870b57cec5SDimitry Andric
16880b57cec5SDimitry Andric if (IsStrnlen) {
1689647cbc5dSDimitry Andric const Expr *maxlenExpr = Call.getArgExpr(1);
16900b57cec5SDimitry Andric SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
16910b57cec5SDimitry Andric
16920b57cec5SDimitry Andric ProgramStateRef stateZeroSize, stateNonZeroSize;
16930b57cec5SDimitry Andric std::tie(stateZeroSize, stateNonZeroSize) =
16940b57cec5SDimitry Andric assumeZero(C, state, maxlenVal, maxlenExpr->getType());
16950b57cec5SDimitry Andric
16960b57cec5SDimitry Andric // If the size can be zero, the result will be 0 in that case, and we don't
16970b57cec5SDimitry Andric // have to check the string itself.
16980b57cec5SDimitry Andric if (stateZeroSize) {
1699647cbc5dSDimitry Andric SVal zero = C.getSValBuilder().makeZeroVal(Call.getResultType());
1700647cbc5dSDimitry Andric stateZeroSize = stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, zero);
17010b57cec5SDimitry Andric C.addTransition(stateZeroSize);
17020b57cec5SDimitry Andric }
17030b57cec5SDimitry Andric
17040b57cec5SDimitry Andric // If the size is GUARANTEED to be zero, we're done!
17050b57cec5SDimitry Andric if (!stateNonZeroSize)
17060b57cec5SDimitry Andric return;
17070b57cec5SDimitry Andric
17080b57cec5SDimitry Andric // Otherwise, record the assumption that the size is nonzero.
17090b57cec5SDimitry Andric state = stateNonZeroSize;
17100b57cec5SDimitry Andric }
17110b57cec5SDimitry Andric
17120b57cec5SDimitry Andric // Check that the string argument is non-null.
1713647cbc5dSDimitry Andric AnyArgExpr Arg = {Call.getArgExpr(0), 0};
17145ffd83dbSDimitry Andric SVal ArgVal = state->getSVal(Arg.Expression, LCtx);
17155ffd83dbSDimitry Andric state = checkNonNull(C, state, Arg, ArgVal);
17160b57cec5SDimitry Andric
17170b57cec5SDimitry Andric if (!state)
17180b57cec5SDimitry Andric return;
17190b57cec5SDimitry Andric
17205ffd83dbSDimitry Andric SVal strLength = getCStringLength(C, state, Arg.Expression, ArgVal);
17210b57cec5SDimitry Andric
17220b57cec5SDimitry Andric // If the argument isn't a valid C string, there's no valid state to
17230b57cec5SDimitry Andric // transition to.
17240b57cec5SDimitry Andric if (strLength.isUndef())
17250b57cec5SDimitry Andric return;
17260b57cec5SDimitry Andric
17270b57cec5SDimitry Andric DefinedOrUnknownSVal result = UnknownVal();
17280b57cec5SDimitry Andric
17290b57cec5SDimitry Andric // If the check is for strnlen() then bind the return value to no more than
17300b57cec5SDimitry Andric // the maxlen value.
17310b57cec5SDimitry Andric if (IsStrnlen) {
17320b57cec5SDimitry Andric QualType cmpTy = C.getSValBuilder().getConditionType();
17330b57cec5SDimitry Andric
17340b57cec5SDimitry Andric // It's a little unfortunate to be getting this again,
17350b57cec5SDimitry Andric // but it's not that expensive...
1736647cbc5dSDimitry Andric const Expr *maxlenExpr = Call.getArgExpr(1);
17370b57cec5SDimitry Andric SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
17380b57cec5SDimitry Andric
1739bdd1243dSDimitry Andric std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
1740bdd1243dSDimitry Andric std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
17410b57cec5SDimitry Andric
17420b57cec5SDimitry Andric if (strLengthNL && maxlenValNL) {
17430b57cec5SDimitry Andric ProgramStateRef stateStringTooLong, stateStringNotTooLong;
17440b57cec5SDimitry Andric
17450b57cec5SDimitry Andric // Check if the strLength is greater than the maxlen.
17460b57cec5SDimitry Andric std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(
17470b57cec5SDimitry Andric C.getSValBuilder()
17480b57cec5SDimitry Andric .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)
17490b57cec5SDimitry Andric .castAs<DefinedOrUnknownSVal>());
17500b57cec5SDimitry Andric
17510b57cec5SDimitry Andric if (stateStringTooLong && !stateStringNotTooLong) {
17520b57cec5SDimitry Andric // If the string is longer than maxlen, return maxlen.
17530b57cec5SDimitry Andric result = *maxlenValNL;
17540b57cec5SDimitry Andric } else if (stateStringNotTooLong && !stateStringTooLong) {
17550b57cec5SDimitry Andric // If the string is shorter than maxlen, return its length.
17560b57cec5SDimitry Andric result = *strLengthNL;
17570b57cec5SDimitry Andric }
17580b57cec5SDimitry Andric }
17590b57cec5SDimitry Andric
17600b57cec5SDimitry Andric if (result.isUnknown()) {
17610b57cec5SDimitry Andric // If we don't have enough information for a comparison, there's
17620b57cec5SDimitry Andric // no guarantee the full string length will actually be returned.
17630b57cec5SDimitry Andric // All we know is the return value is the min of the string length
17640b57cec5SDimitry Andric // and the limit. This is better than nothing.
1765647cbc5dSDimitry Andric result = C.getSValBuilder().conjureSymbolVal(
1766647cbc5dSDimitry Andric nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
17670b57cec5SDimitry Andric NonLoc resultNL = result.castAs<NonLoc>();
17680b57cec5SDimitry Andric
17690b57cec5SDimitry Andric if (strLengthNL) {
17700b57cec5SDimitry Andric state = state->assume(C.getSValBuilder().evalBinOpNN(
17710b57cec5SDimitry Andric state, BO_LE, resultNL, *strLengthNL, cmpTy)
17720b57cec5SDimitry Andric .castAs<DefinedOrUnknownSVal>(), true);
17730b57cec5SDimitry Andric }
17740b57cec5SDimitry Andric
17750b57cec5SDimitry Andric if (maxlenValNL) {
17760b57cec5SDimitry Andric state = state->assume(C.getSValBuilder().evalBinOpNN(
17770b57cec5SDimitry Andric state, BO_LE, resultNL, *maxlenValNL, cmpTy)
17780b57cec5SDimitry Andric .castAs<DefinedOrUnknownSVal>(), true);
17790b57cec5SDimitry Andric }
17800b57cec5SDimitry Andric }
17810b57cec5SDimitry Andric
17820b57cec5SDimitry Andric } else {
17830b57cec5SDimitry Andric // This is a plain strlen(), not strnlen().
17840b57cec5SDimitry Andric result = strLength.castAs<DefinedOrUnknownSVal>();
17850b57cec5SDimitry Andric
17860b57cec5SDimitry Andric // If we don't know the length of the string, conjure a return
17870b57cec5SDimitry Andric // value, so it can be used in constraints, at least.
17880b57cec5SDimitry Andric if (result.isUnknown()) {
1789647cbc5dSDimitry Andric result = C.getSValBuilder().conjureSymbolVal(
1790647cbc5dSDimitry Andric nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
17910b57cec5SDimitry Andric }
17920b57cec5SDimitry Andric }
17930b57cec5SDimitry Andric
17940b57cec5SDimitry Andric // Bind the return value.
17950b57cec5SDimitry Andric assert(!result.isUnknown() && "Should have conjured a value by now");
1796647cbc5dSDimitry Andric state = state->BindExpr(Call.getOriginExpr(), LCtx, result);
17970b57cec5SDimitry Andric C.addTransition(state);
17980b57cec5SDimitry Andric }
17990b57cec5SDimitry Andric
evalStrcpy(CheckerContext & C,const CallEvent & Call) const1800647cbc5dSDimitry Andric void CStringChecker::evalStrcpy(CheckerContext &C,
1801647cbc5dSDimitry Andric const CallEvent &Call) const {
18020b57cec5SDimitry Andric // char *strcpy(char *restrict dst, const char *restrict src);
1803647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1804480093f4SDimitry Andric /* ReturnEnd = */ false,
1805480093f4SDimitry Andric /* IsBounded = */ false,
1806480093f4SDimitry Andric /* appendK = */ ConcatFnKind::none);
18070b57cec5SDimitry Andric }
18080b57cec5SDimitry Andric
evalStrncpy(CheckerContext & C,const CallEvent & Call) const1809647cbc5dSDimitry Andric void CStringChecker::evalStrncpy(CheckerContext &C,
1810647cbc5dSDimitry Andric const CallEvent &Call) const {
18110b57cec5SDimitry Andric // char *strncpy(char *restrict dst, const char *restrict src, size_t n);
1812647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1813480093f4SDimitry Andric /* ReturnEnd = */ false,
1814480093f4SDimitry Andric /* IsBounded = */ true,
1815480093f4SDimitry Andric /* appendK = */ ConcatFnKind::none);
18160b57cec5SDimitry Andric }
18170b57cec5SDimitry Andric
evalStpcpy(CheckerContext & C,const CallEvent & Call) const1818647cbc5dSDimitry Andric void CStringChecker::evalStpcpy(CheckerContext &C,
1819647cbc5dSDimitry Andric const CallEvent &Call) const {
18200b57cec5SDimitry Andric // char *stpcpy(char *restrict dst, const char *restrict src);
1821647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1822480093f4SDimitry Andric /* ReturnEnd = */ true,
1823480093f4SDimitry Andric /* IsBounded = */ false,
1824480093f4SDimitry Andric /* appendK = */ ConcatFnKind::none);
18250b57cec5SDimitry Andric }
18260b57cec5SDimitry Andric
evalStrlcpy(CheckerContext & C,const CallEvent & Call) const1827647cbc5dSDimitry Andric void CStringChecker::evalStrlcpy(CheckerContext &C,
1828647cbc5dSDimitry Andric const CallEvent &Call) const {
1829480093f4SDimitry Andric // size_t strlcpy(char *dest, const char *src, size_t size);
1830647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1831480093f4SDimitry Andric /* ReturnEnd = */ true,
1832480093f4SDimitry Andric /* IsBounded = */ true,
1833480093f4SDimitry Andric /* appendK = */ ConcatFnKind::none,
18340b57cec5SDimitry Andric /* returnPtr = */ false);
18350b57cec5SDimitry Andric }
18360b57cec5SDimitry Andric
evalStrcat(CheckerContext & C,const CallEvent & Call) const1837647cbc5dSDimitry Andric void CStringChecker::evalStrcat(CheckerContext &C,
1838647cbc5dSDimitry Andric const CallEvent &Call) const {
18390b57cec5SDimitry Andric // char *strcat(char *restrict s1, const char *restrict s2);
1840647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1841480093f4SDimitry Andric /* ReturnEnd = */ false,
1842480093f4SDimitry Andric /* IsBounded = */ false,
1843480093f4SDimitry Andric /* appendK = */ ConcatFnKind::strcat);
18440b57cec5SDimitry Andric }
18450b57cec5SDimitry Andric
evalStrncat(CheckerContext & C,const CallEvent & Call) const1846647cbc5dSDimitry Andric void CStringChecker::evalStrncat(CheckerContext &C,
1847647cbc5dSDimitry Andric const CallEvent &Call) const {
18480b57cec5SDimitry Andric // char *strncat(char *restrict s1, const char *restrict s2, size_t n);
1849647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1850480093f4SDimitry Andric /* ReturnEnd = */ false,
1851480093f4SDimitry Andric /* IsBounded = */ true,
1852480093f4SDimitry Andric /* appendK = */ ConcatFnKind::strcat);
18530b57cec5SDimitry Andric }
18540b57cec5SDimitry Andric
evalStrlcat(CheckerContext & C,const CallEvent & Call) const1855647cbc5dSDimitry Andric void CStringChecker::evalStrlcat(CheckerContext &C,
1856647cbc5dSDimitry Andric const CallEvent &Call) const {
1857480093f4SDimitry Andric // size_t strlcat(char *dst, const char *src, size_t size);
1858480093f4SDimitry Andric // It will append at most size - strlen(dst) - 1 bytes,
1859480093f4SDimitry Andric // NULL-terminating the result.
1860647cbc5dSDimitry Andric evalStrcpyCommon(C, Call,
1861480093f4SDimitry Andric /* ReturnEnd = */ false,
1862480093f4SDimitry Andric /* IsBounded = */ true,
1863480093f4SDimitry Andric /* appendK = */ ConcatFnKind::strlcat,
18640b57cec5SDimitry Andric /* returnPtr = */ false);
18650b57cec5SDimitry Andric }
18660b57cec5SDimitry Andric
evalStrcpyCommon(CheckerContext & C,const CallEvent & Call,bool ReturnEnd,bool IsBounded,ConcatFnKind appendK,bool returnPtr) const1867647cbc5dSDimitry Andric void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,
1868480093f4SDimitry Andric bool ReturnEnd, bool IsBounded,
1869480093f4SDimitry Andric ConcatFnKind appendK,
1870480093f4SDimitry Andric bool returnPtr) const {
1871480093f4SDimitry Andric if (appendK == ConcatFnKind::none)
18720b57cec5SDimitry Andric CurrentFunctionDescription = "string copy function";
1873480093f4SDimitry Andric else
1874480093f4SDimitry Andric CurrentFunctionDescription = "string concatenation function";
18755ffd83dbSDimitry Andric
18760b57cec5SDimitry Andric ProgramStateRef state = C.getState();
18770b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
18780b57cec5SDimitry Andric
18790b57cec5SDimitry Andric // Check that the destination is non-null.
1880647cbc5dSDimitry Andric DestinationArgExpr Dst = {{Call.getArgExpr(0), 0}};
18815ffd83dbSDimitry Andric SVal DstVal = state->getSVal(Dst.Expression, LCtx);
18825ffd83dbSDimitry Andric state = checkNonNull(C, state, Dst, DstVal);
18830b57cec5SDimitry Andric if (!state)
18840b57cec5SDimitry Andric return;
18850b57cec5SDimitry Andric
18860b57cec5SDimitry Andric // Check that the source is non-null.
1887647cbc5dSDimitry Andric SourceArgExpr srcExpr = {{Call.getArgExpr(1), 1}};
18885ffd83dbSDimitry Andric SVal srcVal = state->getSVal(srcExpr.Expression, LCtx);
18895ffd83dbSDimitry Andric state = checkNonNull(C, state, srcExpr, srcVal);
18900b57cec5SDimitry Andric if (!state)
18910b57cec5SDimitry Andric return;
18920b57cec5SDimitry Andric
18930b57cec5SDimitry Andric // Get the string length of the source.
18945ffd83dbSDimitry Andric SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);
1895bdd1243dSDimitry Andric std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
1896480093f4SDimitry Andric
1897480093f4SDimitry Andric // Get the string length of the destination buffer.
18985ffd83dbSDimitry Andric SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);
1899bdd1243dSDimitry Andric std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
19000b57cec5SDimitry Andric
19010b57cec5SDimitry Andric // If the source isn't a valid C string, give up.
19020b57cec5SDimitry Andric if (strLength.isUndef())
19030b57cec5SDimitry Andric return;
19040b57cec5SDimitry Andric
19050b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
19060b57cec5SDimitry Andric QualType cmpTy = svalBuilder.getConditionType();
19070b57cec5SDimitry Andric QualType sizeTy = svalBuilder.getContext().getSizeType();
19080b57cec5SDimitry Andric
19090b57cec5SDimitry Andric // These two values allow checking two kinds of errors:
19100b57cec5SDimitry Andric // - actual overflows caused by a source that doesn't fit in the destination
19110b57cec5SDimitry Andric // - potential overflows caused by a bound that could exceed the destination
19120b57cec5SDimitry Andric SVal amountCopied = UnknownVal();
19130b57cec5SDimitry Andric SVal maxLastElementIndex = UnknownVal();
19140b57cec5SDimitry Andric const char *boundWarning = nullptr;
19150b57cec5SDimitry Andric
19165ffd83dbSDimitry Andric // FIXME: Why do we choose the srcExpr if the access has no size?
19175ffd83dbSDimitry Andric // Note that the 3rd argument of the call would be the size parameter.
191806c3fb27SDimitry Andric SizeArgExpr SrcExprAsSizeDummy = {
191906c3fb27SDimitry Andric {srcExpr.Expression, srcExpr.ArgumentIndex}};
19205ffd83dbSDimitry Andric state = CheckOverlap(
19215ffd83dbSDimitry Andric C, state,
1922647cbc5dSDimitry Andric (IsBounded ? SizeArgExpr{{Call.getArgExpr(2), 2}} : SrcExprAsSizeDummy),
1923647cbc5dSDimitry Andric Dst, srcExpr);
19240b57cec5SDimitry Andric
19250b57cec5SDimitry Andric if (!state)
19260b57cec5SDimitry Andric return;
19270b57cec5SDimitry Andric
19280b57cec5SDimitry Andric // If the function is strncpy, strncat, etc... it is bounded.
1929480093f4SDimitry Andric if (IsBounded) {
19300b57cec5SDimitry Andric // Get the max number of characters to copy.
1931647cbc5dSDimitry Andric SizeArgExpr lenExpr = {{Call.getArgExpr(2), 2}};
19325ffd83dbSDimitry Andric SVal lenVal = state->getSVal(lenExpr.Expression, LCtx);
19330b57cec5SDimitry Andric
19340b57cec5SDimitry Andric // Protect against misdeclared strncpy().
19355ffd83dbSDimitry Andric lenVal =
19365ffd83dbSDimitry Andric svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());
19370b57cec5SDimitry Andric
1938bdd1243dSDimitry Andric std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
19390b57cec5SDimitry Andric
19400b57cec5SDimitry Andric // If we know both values, we might be able to figure out how much
19410b57cec5SDimitry Andric // we're copying.
19420b57cec5SDimitry Andric if (strLengthNL && lenValNL) {
1943480093f4SDimitry Andric switch (appendK) {
1944480093f4SDimitry Andric case ConcatFnKind::none:
1945480093f4SDimitry Andric case ConcatFnKind::strcat: {
19460b57cec5SDimitry Andric ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;
19470b57cec5SDimitry Andric // Check if the max number to copy is less than the length of the src.
19480b57cec5SDimitry Andric // If the bound is equal to the source length, strncpy won't null-
19490b57cec5SDimitry Andric // terminate the result!
19500b57cec5SDimitry Andric std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
1951480093f4SDimitry Andric svalBuilder
1952480093f4SDimitry Andric .evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
19530b57cec5SDimitry Andric .castAs<DefinedOrUnknownSVal>());
19540b57cec5SDimitry Andric
19550b57cec5SDimitry Andric if (stateSourceTooLong && !stateSourceNotTooLong) {
1956480093f4SDimitry Andric // Max number to copy is less than the length of the src, so the
1957480093f4SDimitry Andric // actual strLength copied is the max number arg.
19580b57cec5SDimitry Andric state = stateSourceTooLong;
19590b57cec5SDimitry Andric amountCopied = lenVal;
19600b57cec5SDimitry Andric
19610b57cec5SDimitry Andric } else if (!stateSourceTooLong && stateSourceNotTooLong) {
19620b57cec5SDimitry Andric // The source buffer entirely fits in the bound.
19630b57cec5SDimitry Andric state = stateSourceNotTooLong;
19640b57cec5SDimitry Andric amountCopied = strLength;
19650b57cec5SDimitry Andric }
1966480093f4SDimitry Andric break;
1967480093f4SDimitry Andric }
1968480093f4SDimitry Andric case ConcatFnKind::strlcat:
1969480093f4SDimitry Andric if (!dstStrLengthNL)
1970480093f4SDimitry Andric return;
1971480093f4SDimitry Andric
1972480093f4SDimitry Andric // amountCopied = min (size - dstLen - 1 , srcLen)
1973480093f4SDimitry Andric SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
1974480093f4SDimitry Andric *dstStrLengthNL, sizeTy);
197581ad6265SDimitry Andric if (!isa<NonLoc>(freeSpace))
1976480093f4SDimitry Andric return;
1977480093f4SDimitry Andric freeSpace =
1978480093f4SDimitry Andric svalBuilder.evalBinOp(state, BO_Sub, freeSpace,
1979480093f4SDimitry Andric svalBuilder.makeIntVal(1, sizeTy), sizeTy);
1980bdd1243dSDimitry Andric std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();
1981480093f4SDimitry Andric
1982480093f4SDimitry Andric // While unlikely, it is possible that the subtraction is
1983480093f4SDimitry Andric // too complex to compute, let's check whether it succeeded.
1984480093f4SDimitry Andric if (!freeSpaceNL)
1985480093f4SDimitry Andric return;
1986480093f4SDimitry Andric SVal hasEnoughSpace = svalBuilder.evalBinOpNN(
1987480093f4SDimitry Andric state, BO_LE, *strLengthNL, *freeSpaceNL, cmpTy);
1988480093f4SDimitry Andric
1989480093f4SDimitry Andric ProgramStateRef TrueState, FalseState;
1990480093f4SDimitry Andric std::tie(TrueState, FalseState) =
1991480093f4SDimitry Andric state->assume(hasEnoughSpace.castAs<DefinedOrUnknownSVal>());
1992480093f4SDimitry Andric
1993480093f4SDimitry Andric // srcStrLength <= size - dstStrLength -1
1994480093f4SDimitry Andric if (TrueState && !FalseState) {
1995480093f4SDimitry Andric amountCopied = strLength;
19960b57cec5SDimitry Andric }
19970b57cec5SDimitry Andric
1998480093f4SDimitry Andric // srcStrLength > size - dstStrLength -1
1999480093f4SDimitry Andric if (!TrueState && FalseState) {
2000480093f4SDimitry Andric amountCopied = freeSpace;
2001480093f4SDimitry Andric }
2002480093f4SDimitry Andric
2003480093f4SDimitry Andric if (TrueState && FalseState)
2004480093f4SDimitry Andric amountCopied = UnknownVal();
2005480093f4SDimitry Andric break;
2006480093f4SDimitry Andric }
2007480093f4SDimitry Andric }
20080b57cec5SDimitry Andric // We still want to know if the bound is known to be too large.
20090b57cec5SDimitry Andric if (lenValNL) {
2010480093f4SDimitry Andric switch (appendK) {
2011480093f4SDimitry Andric case ConcatFnKind::strcat:
20120b57cec5SDimitry Andric // For strncat, the check is strlen(dst) + lenVal < sizeof(dst)
20130b57cec5SDimitry Andric
20140b57cec5SDimitry Andric // Get the string length of the destination. If the destination is
20150b57cec5SDimitry Andric // memory that can't have a string length, we shouldn't be copying
20160b57cec5SDimitry Andric // into it anyway.
20170b57cec5SDimitry Andric if (dstStrLength.isUndef())
20180b57cec5SDimitry Andric return;
20190b57cec5SDimitry Andric
2020480093f4SDimitry Andric if (dstStrLengthNL) {
2021480093f4SDimitry Andric maxLastElementIndex = svalBuilder.evalBinOpNN(
2022480093f4SDimitry Andric state, BO_Add, *lenValNL, *dstStrLengthNL, sizeTy);
2023480093f4SDimitry Andric
20240b57cec5SDimitry Andric boundWarning = "Size argument is greater than the free space in the "
20250b57cec5SDimitry Andric "destination buffer";
20260b57cec5SDimitry Andric }
2027480093f4SDimitry Andric break;
2028480093f4SDimitry Andric case ConcatFnKind::none:
2029480093f4SDimitry Andric case ConcatFnKind::strlcat:
2030480093f4SDimitry Andric // For strncpy and strlcat, this is just checking
2031480093f4SDimitry Andric // that lenVal <= sizeof(dst).
20320b57cec5SDimitry Andric // (Yes, strncpy and strncat differ in how they treat termination.
20330b57cec5SDimitry Andric // strncat ALWAYS terminates, but strncpy doesn't.)
20340b57cec5SDimitry Andric
20350b57cec5SDimitry Andric // We need a special case for when the copy size is zero, in which
20360b57cec5SDimitry Andric // case strncpy will do no work at all. Our bounds check uses n-1
20370b57cec5SDimitry Andric // as the last element accessed, so n == 0 is problematic.
20380b57cec5SDimitry Andric ProgramStateRef StateZeroSize, StateNonZeroSize;
20390b57cec5SDimitry Andric std::tie(StateZeroSize, StateNonZeroSize) =
20400b57cec5SDimitry Andric assumeZero(C, state, *lenValNL, sizeTy);
20410b57cec5SDimitry Andric
20420b57cec5SDimitry Andric // If the size is known to be zero, we're done.
20430b57cec5SDimitry Andric if (StateZeroSize && !StateNonZeroSize) {
20440b57cec5SDimitry Andric if (returnPtr) {
2045647cbc5dSDimitry Andric StateZeroSize =
2046647cbc5dSDimitry Andric StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, DstVal);
20470b57cec5SDimitry Andric } else {
2048480093f4SDimitry Andric if (appendK == ConcatFnKind::none) {
2049480093f4SDimitry Andric // strlcpy returns strlen(src)
2050647cbc5dSDimitry Andric StateZeroSize = StateZeroSize->BindExpr(Call.getOriginExpr(),
2051647cbc5dSDimitry Andric LCtx, strLength);
2052480093f4SDimitry Andric } else {
2053480093f4SDimitry Andric // strlcat returns strlen(src) + strlen(dst)
2054480093f4SDimitry Andric SVal retSize = svalBuilder.evalBinOp(
2055480093f4SDimitry Andric state, BO_Add, strLength, dstStrLength, sizeTy);
2056647cbc5dSDimitry Andric StateZeroSize =
2057647cbc5dSDimitry Andric StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, retSize);
2058480093f4SDimitry Andric }
20590b57cec5SDimitry Andric }
20600b57cec5SDimitry Andric C.addTransition(StateZeroSize);
20610b57cec5SDimitry Andric return;
20620b57cec5SDimitry Andric }
20630b57cec5SDimitry Andric
20640b57cec5SDimitry Andric // Otherwise, go ahead and figure out the last element we'll touch.
20650b57cec5SDimitry Andric // We don't record the non-zero assumption here because we can't
20660b57cec5SDimitry Andric // be sure. We won't warn on a possible zero.
20670b57cec5SDimitry Andric NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
2068480093f4SDimitry Andric maxLastElementIndex =
2069480093f4SDimitry Andric svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy);
20700b57cec5SDimitry Andric boundWarning = "Size argument is greater than the length of the "
20710b57cec5SDimitry Andric "destination buffer";
2072480093f4SDimitry Andric break;
20730b57cec5SDimitry Andric }
20740b57cec5SDimitry Andric }
20750b57cec5SDimitry Andric } else {
20760b57cec5SDimitry Andric // The function isn't bounded. The amount copied should match the length
20770b57cec5SDimitry Andric // of the source buffer.
20780b57cec5SDimitry Andric amountCopied = strLength;
20790b57cec5SDimitry Andric }
20800b57cec5SDimitry Andric
20810b57cec5SDimitry Andric assert(state);
20820b57cec5SDimitry Andric
20830b57cec5SDimitry Andric // This represents the number of characters copied into the destination
20840b57cec5SDimitry Andric // buffer. (It may not actually be the strlen if the destination buffer
20850b57cec5SDimitry Andric // is not terminated.)
20860b57cec5SDimitry Andric SVal finalStrLength = UnknownVal();
2087480093f4SDimitry Andric SVal strlRetVal = UnknownVal();
2088480093f4SDimitry Andric
2089480093f4SDimitry Andric if (appendK == ConcatFnKind::none && !returnPtr) {
2090480093f4SDimitry Andric // strlcpy returns the sizeof(src)
2091480093f4SDimitry Andric strlRetVal = strLength;
2092480093f4SDimitry Andric }
20930b57cec5SDimitry Andric
20940b57cec5SDimitry Andric // If this is an appending function (strcat, strncat...) then set the
20950b57cec5SDimitry Andric // string length to strlen(src) + strlen(dst) since the buffer will
20960b57cec5SDimitry Andric // ultimately contain both.
2097480093f4SDimitry Andric if (appendK != ConcatFnKind::none) {
20980b57cec5SDimitry Andric // Get the string length of the destination. If the destination is memory
20990b57cec5SDimitry Andric // that can't have a string length, we shouldn't be copying into it anyway.
21000b57cec5SDimitry Andric if (dstStrLength.isUndef())
21010b57cec5SDimitry Andric return;
21020b57cec5SDimitry Andric
2103480093f4SDimitry Andric if (appendK == ConcatFnKind::strlcat && dstStrLengthNL && strLengthNL) {
2104480093f4SDimitry Andric strlRetVal = svalBuilder.evalBinOpNN(state, BO_Add, *strLengthNL,
2105480093f4SDimitry Andric *dstStrLengthNL, sizeTy);
2106480093f4SDimitry Andric }
2107480093f4SDimitry Andric
2108bdd1243dSDimitry Andric std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();
21090b57cec5SDimitry Andric
21100b57cec5SDimitry Andric // If we know both string lengths, we might know the final string length.
2111480093f4SDimitry Andric if (amountCopiedNL && dstStrLengthNL) {
21120b57cec5SDimitry Andric // Make sure the two lengths together don't overflow a size_t.
2113480093f4SDimitry Andric state = checkAdditionOverflow(C, state, *amountCopiedNL, *dstStrLengthNL);
21140b57cec5SDimitry Andric if (!state)
21150b57cec5SDimitry Andric return;
21160b57cec5SDimitry Andric
2117480093f4SDimitry Andric finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *amountCopiedNL,
21180b57cec5SDimitry Andric *dstStrLengthNL, sizeTy);
21190b57cec5SDimitry Andric }
21200b57cec5SDimitry Andric
21210b57cec5SDimitry Andric // If we couldn't get a single value for the final string length,
21220b57cec5SDimitry Andric // we can at least bound it by the individual lengths.
21230b57cec5SDimitry Andric if (finalStrLength.isUnknown()) {
21240b57cec5SDimitry Andric // Try to get a "hypothetical" string length symbol, which we can later
21250b57cec5SDimitry Andric // set as a real value if that turns out to be the case.
2126647cbc5dSDimitry Andric finalStrLength =
2127647cbc5dSDimitry Andric getCStringLength(C, state, Call.getOriginExpr(), DstVal, true);
21280b57cec5SDimitry Andric assert(!finalStrLength.isUndef());
21290b57cec5SDimitry Andric
2130bdd1243dSDimitry Andric if (std::optional<NonLoc> finalStrLengthNL =
2131bdd1243dSDimitry Andric finalStrLength.getAs<NonLoc>()) {
2132480093f4SDimitry Andric if (amountCopiedNL && appendK == ConcatFnKind::none) {
2133480093f4SDimitry Andric // we overwrite dst string with the src
21340b57cec5SDimitry Andric // finalStrLength >= srcStrLength
2135480093f4SDimitry Andric SVal sourceInResult = svalBuilder.evalBinOpNN(
2136480093f4SDimitry Andric state, BO_GE, *finalStrLengthNL, *amountCopiedNL, cmpTy);
21370b57cec5SDimitry Andric state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(),
21380b57cec5SDimitry Andric true);
21390b57cec5SDimitry Andric if (!state)
21400b57cec5SDimitry Andric return;
21410b57cec5SDimitry Andric }
21420b57cec5SDimitry Andric
2143480093f4SDimitry Andric if (dstStrLengthNL && appendK != ConcatFnKind::none) {
2144480093f4SDimitry Andric // we extend the dst string with the src
21450b57cec5SDimitry Andric // finalStrLength >= dstStrLength
21460b57cec5SDimitry Andric SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE,
21470b57cec5SDimitry Andric *finalStrLengthNL,
21480b57cec5SDimitry Andric *dstStrLengthNL,
21490b57cec5SDimitry Andric cmpTy);
21500b57cec5SDimitry Andric state =
21510b57cec5SDimitry Andric state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true);
21520b57cec5SDimitry Andric if (!state)
21530b57cec5SDimitry Andric return;
21540b57cec5SDimitry Andric }
21550b57cec5SDimitry Andric }
21560b57cec5SDimitry Andric }
21570b57cec5SDimitry Andric
21580b57cec5SDimitry Andric } else {
21590b57cec5SDimitry Andric // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and
21600b57cec5SDimitry Andric // the final string length will match the input string length.
21610b57cec5SDimitry Andric finalStrLength = amountCopied;
21620b57cec5SDimitry Andric }
21630b57cec5SDimitry Andric
21640b57cec5SDimitry Andric SVal Result;
21650b57cec5SDimitry Andric
21660b57cec5SDimitry Andric if (returnPtr) {
21670b57cec5SDimitry Andric // The final result of the function will either be a pointer past the last
21680b57cec5SDimitry Andric // copied element, or a pointer to the start of the destination buffer.
2169480093f4SDimitry Andric Result = (ReturnEnd ? UnknownVal() : DstVal);
21700b57cec5SDimitry Andric } else {
2171480093f4SDimitry Andric if (appendK == ConcatFnKind::strlcat || appendK == ConcatFnKind::none)
2172480093f4SDimitry Andric //strlcpy, strlcat
2173480093f4SDimitry Andric Result = strlRetVal;
2174480093f4SDimitry Andric else
21750b57cec5SDimitry Andric Result = finalStrLength;
21760b57cec5SDimitry Andric }
21770b57cec5SDimitry Andric
21780b57cec5SDimitry Andric assert(state);
21790b57cec5SDimitry Andric
21800b57cec5SDimitry Andric // If the destination is a MemRegion, try to check for a buffer overflow and
21810b57cec5SDimitry Andric // record the new string length.
2182bdd1243dSDimitry Andric if (std::optional<loc::MemRegionVal> dstRegVal =
21830b57cec5SDimitry Andric DstVal.getAs<loc::MemRegionVal>()) {
21845ffd83dbSDimitry Andric QualType ptrTy = Dst.Expression->getType();
21850b57cec5SDimitry Andric
21860b57cec5SDimitry Andric // If we have an exact value on a bounded copy, use that to check for
21870b57cec5SDimitry Andric // overflows, rather than our estimate about how much is actually copied.
2188bdd1243dSDimitry Andric if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
21895ffd83dbSDimitry Andric SVal maxLastElement =
21905ffd83dbSDimitry Andric svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);
21915ffd83dbSDimitry Andric
21925f757f3fSDimitry Andric // Check if the first byte of the destination is writable.
21935f757f3fSDimitry Andric state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);
21945f757f3fSDimitry Andric if (!state)
21955f757f3fSDimitry Andric return;
21965f757f3fSDimitry Andric // Check if the last byte of the destination is writable.
21975ffd83dbSDimitry Andric state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write);
21980b57cec5SDimitry Andric if (!state)
21990b57cec5SDimitry Andric return;
22000b57cec5SDimitry Andric }
22010b57cec5SDimitry Andric
22020b57cec5SDimitry Andric // Then, if the final length is known...
2203bdd1243dSDimitry Andric if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
22040b57cec5SDimitry Andric SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
22050b57cec5SDimitry Andric *knownStrLength, ptrTy);
22060b57cec5SDimitry Andric
22070b57cec5SDimitry Andric // ...and we haven't checked the bound, we'll check the actual copy.
22080b57cec5SDimitry Andric if (!boundWarning) {
22095f757f3fSDimitry Andric // Check if the first byte of the destination is writable.
22105f757f3fSDimitry Andric state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);
22115f757f3fSDimitry Andric if (!state)
22125f757f3fSDimitry Andric return;
22135f757f3fSDimitry Andric // Check if the last byte of the destination is writable.
22145ffd83dbSDimitry Andric state = CheckLocation(C, state, Dst, lastElement, AccessKind::write);
22150b57cec5SDimitry Andric if (!state)
22160b57cec5SDimitry Andric return;
22170b57cec5SDimitry Andric }
22180b57cec5SDimitry Andric
22190b57cec5SDimitry Andric // If this is a stpcpy-style copy, the last element is the return value.
2220480093f4SDimitry Andric if (returnPtr && ReturnEnd)
22210b57cec5SDimitry Andric Result = lastElement;
22220b57cec5SDimitry Andric }
22230b57cec5SDimitry Andric
22240b57cec5SDimitry Andric // Invalidate the destination (regular invalidation without pointer-escaping
22250b57cec5SDimitry Andric // the address of the top-level region). This must happen before we set the
22260b57cec5SDimitry Andric // C string length because invalidation will clear the length.
22270b57cec5SDimitry Andric // FIXME: Even if we can't perfectly model the copy, we should see if we
22280b57cec5SDimitry Andric // can use LazyCompoundVals to copy the source values into the destination.
22290b57cec5SDimitry Andric // This would probably remove any existing bindings past the end of the
22300b57cec5SDimitry Andric // string, but that's still an improvement over blank invalidation.
223106c3fb27SDimitry Andric state = invalidateDestinationBufferBySize(C, state, Dst.Expression,
223206c3fb27SDimitry Andric *dstRegVal, amountCopied,
223306c3fb27SDimitry Andric C.getASTContext().getSizeType());
22340b57cec5SDimitry Andric
22350b57cec5SDimitry Andric // Invalidate the source (const-invalidation without const-pointer-escaping
22360b57cec5SDimitry Andric // the address of the top-level region).
223706c3fb27SDimitry Andric state = invalidateSourceBuffer(C, state, srcExpr.Expression, srcVal);
22380b57cec5SDimitry Andric
22390b57cec5SDimitry Andric // Set the C string length of the destination, if we know it.
2240480093f4SDimitry Andric if (IsBounded && (appendK == ConcatFnKind::none)) {
22410b57cec5SDimitry Andric // strncpy is annoying in that it doesn't guarantee to null-terminate
22420b57cec5SDimitry Andric // the result string. If the original string didn't fit entirely inside
22430b57cec5SDimitry Andric // the bound (including the null-terminator), we don't know how long the
22440b57cec5SDimitry Andric // result is.
22450b57cec5SDimitry Andric if (amountCopied != strLength)
22460b57cec5SDimitry Andric finalStrLength = UnknownVal();
22470b57cec5SDimitry Andric }
22480b57cec5SDimitry Andric state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength);
22490b57cec5SDimitry Andric }
22500b57cec5SDimitry Andric
22510b57cec5SDimitry Andric assert(state);
22520b57cec5SDimitry Andric
22530b57cec5SDimitry Andric if (returnPtr) {
22540b57cec5SDimitry Andric // If this is a stpcpy-style copy, but we were unable to check for a buffer
22550b57cec5SDimitry Andric // overflow, we still need a result. Conjure a return value.
2256480093f4SDimitry Andric if (ReturnEnd && Result.isUnknown()) {
2257647cbc5dSDimitry Andric Result = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
2258647cbc5dSDimitry Andric C.blockCount());
22590b57cec5SDimitry Andric }
22600b57cec5SDimitry Andric }
22610b57cec5SDimitry Andric // Set the return value.
2262647cbc5dSDimitry Andric state = state->BindExpr(Call.getOriginExpr(), LCtx, Result);
22630b57cec5SDimitry Andric C.addTransition(state);
22640b57cec5SDimitry Andric }
22650b57cec5SDimitry Andric
evalStrcmp(CheckerContext & C,const CallEvent & Call) const2266647cbc5dSDimitry Andric void CStringChecker::evalStrcmp(CheckerContext &C,
2267647cbc5dSDimitry Andric const CallEvent &Call) const {
22680b57cec5SDimitry Andric //int strcmp(const char *s1, const char *s2);
2269647cbc5dSDimitry Andric evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ false);
22700b57cec5SDimitry Andric }
22710b57cec5SDimitry Andric
evalStrncmp(CheckerContext & C,const CallEvent & Call) const2272647cbc5dSDimitry Andric void CStringChecker::evalStrncmp(CheckerContext &C,
2273647cbc5dSDimitry Andric const CallEvent &Call) const {
22740b57cec5SDimitry Andric //int strncmp(const char *s1, const char *s2, size_t n);
2275647cbc5dSDimitry Andric evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ false);
22760b57cec5SDimitry Andric }
22770b57cec5SDimitry Andric
evalStrcasecmp(CheckerContext & C,const CallEvent & Call) const22780b57cec5SDimitry Andric void CStringChecker::evalStrcasecmp(CheckerContext &C,
2279647cbc5dSDimitry Andric const CallEvent &Call) const {
22800b57cec5SDimitry Andric //int strcasecmp(const char *s1, const char *s2);
2281647cbc5dSDimitry Andric evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ true);
22820b57cec5SDimitry Andric }
22830b57cec5SDimitry Andric
evalStrncasecmp(CheckerContext & C,const CallEvent & Call) const22840b57cec5SDimitry Andric void CStringChecker::evalStrncasecmp(CheckerContext &C,
2285647cbc5dSDimitry Andric const CallEvent &Call) const {
22860b57cec5SDimitry Andric //int strncasecmp(const char *s1, const char *s2, size_t n);
2287647cbc5dSDimitry Andric evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ true);
22880b57cec5SDimitry Andric }
22890b57cec5SDimitry Andric
evalStrcmpCommon(CheckerContext & C,const CallEvent & Call,bool IsBounded,bool IgnoreCase) const2290647cbc5dSDimitry Andric void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,
2291480093f4SDimitry Andric bool IsBounded, bool IgnoreCase) const {
22920b57cec5SDimitry Andric CurrentFunctionDescription = "string comparison function";
22930b57cec5SDimitry Andric ProgramStateRef state = C.getState();
22940b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
22950b57cec5SDimitry Andric
22960b57cec5SDimitry Andric // Check that the first string is non-null
2297647cbc5dSDimitry Andric AnyArgExpr Left = {Call.getArgExpr(0), 0};
22985ffd83dbSDimitry Andric SVal LeftVal = state->getSVal(Left.Expression, LCtx);
22995ffd83dbSDimitry Andric state = checkNonNull(C, state, Left, LeftVal);
23000b57cec5SDimitry Andric if (!state)
23010b57cec5SDimitry Andric return;
23020b57cec5SDimitry Andric
23030b57cec5SDimitry Andric // Check that the second string is non-null.
2304647cbc5dSDimitry Andric AnyArgExpr Right = {Call.getArgExpr(1), 1};
23055ffd83dbSDimitry Andric SVal RightVal = state->getSVal(Right.Expression, LCtx);
23065ffd83dbSDimitry Andric state = checkNonNull(C, state, Right, RightVal);
23070b57cec5SDimitry Andric if (!state)
23080b57cec5SDimitry Andric return;
23090b57cec5SDimitry Andric
23100b57cec5SDimitry Andric // Get the string length of the first string or give up.
23115ffd83dbSDimitry Andric SVal LeftLength = getCStringLength(C, state, Left.Expression, LeftVal);
23125ffd83dbSDimitry Andric if (LeftLength.isUndef())
23130b57cec5SDimitry Andric return;
23140b57cec5SDimitry Andric
23150b57cec5SDimitry Andric // Get the string length of the second string or give up.
23165ffd83dbSDimitry Andric SVal RightLength = getCStringLength(C, state, Right.Expression, RightVal);
23175ffd83dbSDimitry Andric if (RightLength.isUndef())
23180b57cec5SDimitry Andric return;
23190b57cec5SDimitry Andric
23200b57cec5SDimitry Andric // If we know the two buffers are the same, we know the result is 0.
23210b57cec5SDimitry Andric // First, get the two buffers' addresses. Another checker will have already
23220b57cec5SDimitry Andric // made sure they're not undefined.
23235ffd83dbSDimitry Andric DefinedOrUnknownSVal LV = LeftVal.castAs<DefinedOrUnknownSVal>();
23245ffd83dbSDimitry Andric DefinedOrUnknownSVal RV = RightVal.castAs<DefinedOrUnknownSVal>();
23250b57cec5SDimitry Andric
23260b57cec5SDimitry Andric // See if they are the same.
23270b57cec5SDimitry Andric SValBuilder &svalBuilder = C.getSValBuilder();
23280b57cec5SDimitry Andric DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
23290b57cec5SDimitry Andric ProgramStateRef StSameBuf, StNotSameBuf;
23300b57cec5SDimitry Andric std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
23310b57cec5SDimitry Andric
23320b57cec5SDimitry Andric // If the two arguments might be the same buffer, we know the result is 0,
23330b57cec5SDimitry Andric // and we only need to check one size.
23340b57cec5SDimitry Andric if (StSameBuf) {
2335647cbc5dSDimitry Andric StSameBuf =
2336647cbc5dSDimitry Andric StSameBuf->BindExpr(Call.getOriginExpr(), LCtx,
2337647cbc5dSDimitry Andric svalBuilder.makeZeroVal(Call.getResultType()));
23380b57cec5SDimitry Andric C.addTransition(StSameBuf);
23390b57cec5SDimitry Andric
23400b57cec5SDimitry Andric // If the two arguments are GUARANTEED to be the same, we're done!
23410b57cec5SDimitry Andric if (!StNotSameBuf)
23420b57cec5SDimitry Andric return;
23430b57cec5SDimitry Andric }
23440b57cec5SDimitry Andric
23450b57cec5SDimitry Andric assert(StNotSameBuf);
23460b57cec5SDimitry Andric state = StNotSameBuf;
23470b57cec5SDimitry Andric
23480b57cec5SDimitry Andric // At this point we can go about comparing the two buffers.
23490b57cec5SDimitry Andric // For now, we only do this if they're both known string literals.
23500b57cec5SDimitry Andric
23510b57cec5SDimitry Andric // Attempt to extract string literals from both expressions.
23525ffd83dbSDimitry Andric const StringLiteral *LeftStrLiteral =
23535ffd83dbSDimitry Andric getCStringLiteral(C, state, Left.Expression, LeftVal);
23545ffd83dbSDimitry Andric const StringLiteral *RightStrLiteral =
23555ffd83dbSDimitry Andric getCStringLiteral(C, state, Right.Expression, RightVal);
23560b57cec5SDimitry Andric bool canComputeResult = false;
2357647cbc5dSDimitry Andric SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, Call.getOriginExpr(),
2358647cbc5dSDimitry Andric LCtx, C.blockCount());
23590b57cec5SDimitry Andric
23605ffd83dbSDimitry Andric if (LeftStrLiteral && RightStrLiteral) {
23615ffd83dbSDimitry Andric StringRef LeftStrRef = LeftStrLiteral->getString();
23625ffd83dbSDimitry Andric StringRef RightStrRef = RightStrLiteral->getString();
23630b57cec5SDimitry Andric
2364480093f4SDimitry Andric if (IsBounded) {
23650b57cec5SDimitry Andric // Get the max number of characters to compare.
2366647cbc5dSDimitry Andric const Expr *lenExpr = Call.getArgExpr(2);
23670b57cec5SDimitry Andric SVal lenVal = state->getSVal(lenExpr, LCtx);
23680b57cec5SDimitry Andric
23690b57cec5SDimitry Andric // If the length is known, we can get the right substrings.
23700b57cec5SDimitry Andric if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) {
23710b57cec5SDimitry Andric // Create substrings of each to compare the prefix.
23725ffd83dbSDimitry Andric LeftStrRef = LeftStrRef.substr(0, (size_t)len->getZExtValue());
23735ffd83dbSDimitry Andric RightStrRef = RightStrRef.substr(0, (size_t)len->getZExtValue());
23740b57cec5SDimitry Andric canComputeResult = true;
23750b57cec5SDimitry Andric }
23760b57cec5SDimitry Andric } else {
23770b57cec5SDimitry Andric // This is a normal, unbounded strcmp.
23780b57cec5SDimitry Andric canComputeResult = true;
23790b57cec5SDimitry Andric }
23800b57cec5SDimitry Andric
23810b57cec5SDimitry Andric if (canComputeResult) {
23820b57cec5SDimitry Andric // Real strcmp stops at null characters.
23835ffd83dbSDimitry Andric size_t s1Term = LeftStrRef.find('\0');
23840b57cec5SDimitry Andric if (s1Term != StringRef::npos)
23855ffd83dbSDimitry Andric LeftStrRef = LeftStrRef.substr(0, s1Term);
23860b57cec5SDimitry Andric
23875ffd83dbSDimitry Andric size_t s2Term = RightStrRef.find('\0');
23880b57cec5SDimitry Andric if (s2Term != StringRef::npos)
23895ffd83dbSDimitry Andric RightStrRef = RightStrRef.substr(0, s2Term);
23900b57cec5SDimitry Andric
23910b57cec5SDimitry Andric // Use StringRef's comparison methods to compute the actual result.
2392fe6060f1SDimitry Andric int compareRes = IgnoreCase ? LeftStrRef.compare_insensitive(RightStrRef)
23935ffd83dbSDimitry Andric : LeftStrRef.compare(RightStrRef);
23940b57cec5SDimitry Andric
23950b57cec5SDimitry Andric // The strcmp function returns an integer greater than, equal to, or less
23960b57cec5SDimitry Andric // than zero, [c11, p7.24.4.2].
23970b57cec5SDimitry Andric if (compareRes == 0) {
2398647cbc5dSDimitry Andric resultVal = svalBuilder.makeIntVal(compareRes, Call.getResultType());
23990b57cec5SDimitry Andric }
24000b57cec5SDimitry Andric else {
2401647cbc5dSDimitry Andric DefinedSVal zeroVal = svalBuilder.makeIntVal(0, Call.getResultType());
24020b57cec5SDimitry Andric // Constrain strcmp's result range based on the result of StringRef's
24030b57cec5SDimitry Andric // comparison methods.
2404bdd1243dSDimitry Andric BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT;
24050b57cec5SDimitry Andric SVal compareWithZero =
24060b57cec5SDimitry Andric svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
24070b57cec5SDimitry Andric svalBuilder.getConditionType());
24080b57cec5SDimitry Andric DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>();
24090b57cec5SDimitry Andric state = state->assume(compareWithZeroVal, true);
24100b57cec5SDimitry Andric }
24110b57cec5SDimitry Andric }
24120b57cec5SDimitry Andric }
24130b57cec5SDimitry Andric
2414647cbc5dSDimitry Andric state = state->BindExpr(Call.getOriginExpr(), LCtx, resultVal);
24150b57cec5SDimitry Andric
24160b57cec5SDimitry Andric // Record this as a possible path.
24170b57cec5SDimitry Andric C.addTransition(state);
24180b57cec5SDimitry Andric }
24190b57cec5SDimitry Andric
evalStrsep(CheckerContext & C,const CallEvent & Call) const2420647cbc5dSDimitry Andric void CStringChecker::evalStrsep(CheckerContext &C,
2421647cbc5dSDimitry Andric const CallEvent &Call) const {
24220b57cec5SDimitry Andric // char *strsep(char **stringp, const char *delim);
24235e801ac6SDimitry Andric // Verify whether the search string parameter matches the return type.
2424647cbc5dSDimitry Andric SourceArgExpr SearchStrPtr = {{Call.getArgExpr(0), 0}};
24255ffd83dbSDimitry Andric
24265ffd83dbSDimitry Andric QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType();
2427647cbc5dSDimitry Andric if (CharPtrTy.isNull() || Call.getResultType().getUnqualifiedType() !=
2428647cbc5dSDimitry Andric CharPtrTy.getUnqualifiedType())
24290b57cec5SDimitry Andric return;
24300b57cec5SDimitry Andric
24310b57cec5SDimitry Andric CurrentFunctionDescription = "strsep()";
24320b57cec5SDimitry Andric ProgramStateRef State = C.getState();
24330b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
24340b57cec5SDimitry Andric
24350b57cec5SDimitry Andric // Check that the search string pointer is non-null (though it may point to
24360b57cec5SDimitry Andric // a null string).
24375ffd83dbSDimitry Andric SVal SearchStrVal = State->getSVal(SearchStrPtr.Expression, LCtx);
24385ffd83dbSDimitry Andric State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
24390b57cec5SDimitry Andric if (!State)
24400b57cec5SDimitry Andric return;
24410b57cec5SDimitry Andric
24420b57cec5SDimitry Andric // Check that the delimiter string is non-null.
2443647cbc5dSDimitry Andric AnyArgExpr DelimStr = {Call.getArgExpr(1), 1};
24445ffd83dbSDimitry Andric SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx);
24455ffd83dbSDimitry Andric State = checkNonNull(C, State, DelimStr, DelimStrVal);
24460b57cec5SDimitry Andric if (!State)
24470b57cec5SDimitry Andric return;
24480b57cec5SDimitry Andric
24490b57cec5SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
24500b57cec5SDimitry Andric SVal Result;
2451bdd1243dSDimitry Andric if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
24520b57cec5SDimitry Andric // Get the current value of the search string pointer, as a char*.
24530b57cec5SDimitry Andric Result = State->getSVal(*SearchStrLoc, CharPtrTy);
24540b57cec5SDimitry Andric
24550b57cec5SDimitry Andric // Invalidate the search string, representing the change of one delimiter
24560b57cec5SDimitry Andric // character to NUL.
245706c3fb27SDimitry Andric // As the replacement never overflows, do not invalidate its super region.
245806c3fb27SDimitry Andric State = invalidateDestinationBufferNeverOverflows(
245906c3fb27SDimitry Andric C, State, SearchStrPtr.Expression, Result);
24600b57cec5SDimitry Andric
24610b57cec5SDimitry Andric // Overwrite the search string pointer. The new value is either an address
24620b57cec5SDimitry Andric // further along in the same string, or NULL if there are no more tokens.
2463647cbc5dSDimitry Andric State =
2464647cbc5dSDimitry Andric State->bindLoc(*SearchStrLoc,
2465647cbc5dSDimitry Andric SVB.conjureSymbolVal(getTag(), Call.getOriginExpr(),
2466647cbc5dSDimitry Andric LCtx, CharPtrTy, C.blockCount()),
24670b57cec5SDimitry Andric LCtx);
24680b57cec5SDimitry Andric } else {
24690b57cec5SDimitry Andric assert(SearchStrVal.isUnknown());
24700b57cec5SDimitry Andric // Conjure a symbolic value. It's the best we can do.
2471647cbc5dSDimitry Andric Result = SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx,
2472647cbc5dSDimitry Andric C.blockCount());
24730b57cec5SDimitry Andric }
24740b57cec5SDimitry Andric
24750b57cec5SDimitry Andric // Set the return value, and finish.
2476647cbc5dSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), LCtx, Result);
24770b57cec5SDimitry Andric C.addTransition(State);
24780b57cec5SDimitry Andric }
24790b57cec5SDimitry Andric
24800b57cec5SDimitry Andric // These should probably be moved into a C++ standard library checker.
evalStdCopy(CheckerContext & C,const CallEvent & Call) const2481647cbc5dSDimitry Andric void CStringChecker::evalStdCopy(CheckerContext &C,
2482647cbc5dSDimitry Andric const CallEvent &Call) const {
2483647cbc5dSDimitry Andric evalStdCopyCommon(C, Call);
24840b57cec5SDimitry Andric }
24850b57cec5SDimitry Andric
evalStdCopyBackward(CheckerContext & C,const CallEvent & Call) const24860b57cec5SDimitry Andric void CStringChecker::evalStdCopyBackward(CheckerContext &C,
2487647cbc5dSDimitry Andric const CallEvent &Call) const {
2488647cbc5dSDimitry Andric evalStdCopyCommon(C, Call);
24890b57cec5SDimitry Andric }
24900b57cec5SDimitry Andric
evalStdCopyCommon(CheckerContext & C,const CallEvent & Call) const24910b57cec5SDimitry Andric void CStringChecker::evalStdCopyCommon(CheckerContext &C,
2492647cbc5dSDimitry Andric const CallEvent &Call) const {
2493647cbc5dSDimitry Andric if (!Call.getArgExpr(2)->getType()->isPointerType())
24940b57cec5SDimitry Andric return;
24950b57cec5SDimitry Andric
24960b57cec5SDimitry Andric ProgramStateRef State = C.getState();
24970b57cec5SDimitry Andric
24980b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
24990b57cec5SDimitry Andric
25000b57cec5SDimitry Andric // template <class _InputIterator, class _OutputIterator>
25010b57cec5SDimitry Andric // _OutputIterator
25020b57cec5SDimitry Andric // copy(_InputIterator __first, _InputIterator __last,
25030b57cec5SDimitry Andric // _OutputIterator __result)
25040b57cec5SDimitry Andric
25050b57cec5SDimitry Andric // Invalidate the destination buffer
2506647cbc5dSDimitry Andric const Expr *Dst = Call.getArgExpr(2);
25070b57cec5SDimitry Andric SVal DstVal = State->getSVal(Dst, LCtx);
250806c3fb27SDimitry Andric // FIXME: As we do not know how many items are copied, we also invalidate the
250906c3fb27SDimitry Andric // super region containing the target location.
251006c3fb27SDimitry Andric State =
251106c3fb27SDimitry Andric invalidateDestinationBufferAlwaysEscapeSuperRegion(C, State, Dst, DstVal);
25120b57cec5SDimitry Andric
25130b57cec5SDimitry Andric SValBuilder &SVB = C.getSValBuilder();
25140b57cec5SDimitry Andric
2515647cbc5dSDimitry Andric SVal ResultVal =
2516647cbc5dSDimitry Andric SVB.conjureSymbolVal(nullptr, Call.getOriginExpr(), LCtx, C.blockCount());
2517647cbc5dSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), LCtx, ResultVal);
25180b57cec5SDimitry Andric
25190b57cec5SDimitry Andric C.addTransition(State);
25200b57cec5SDimitry Andric }
25210b57cec5SDimitry Andric
evalMemset(CheckerContext & C,const CallEvent & Call) const2522647cbc5dSDimitry Andric void CStringChecker::evalMemset(CheckerContext &C,
2523647cbc5dSDimitry Andric const CallEvent &Call) const {
25245ffd83dbSDimitry Andric // void *memset(void *s, int c, size_t n);
25250b57cec5SDimitry Andric CurrentFunctionDescription = "memory set function";
25260b57cec5SDimitry Andric
2527647cbc5dSDimitry Andric DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};
2528647cbc5dSDimitry Andric AnyArgExpr CharE = {Call.getArgExpr(1), 1};
2529647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(2), 2}};
25305ffd83dbSDimitry Andric
25310b57cec5SDimitry Andric ProgramStateRef State = C.getState();
25320b57cec5SDimitry Andric
25330b57cec5SDimitry Andric // See if the size argument is zero.
25340b57cec5SDimitry Andric const LocationContext *LCtx = C.getLocationContext();
25355ffd83dbSDimitry Andric SVal SizeVal = C.getSVal(Size.Expression);
25365ffd83dbSDimitry Andric QualType SizeTy = Size.Expression->getType();
25370b57cec5SDimitry Andric
25385ffd83dbSDimitry Andric ProgramStateRef ZeroSize, NonZeroSize;
25395ffd83dbSDimitry Andric std::tie(ZeroSize, NonZeroSize) = assumeZero(C, State, SizeVal, SizeTy);
25400b57cec5SDimitry Andric
25410b57cec5SDimitry Andric // Get the value of the memory area.
25425ffd83dbSDimitry Andric SVal BufferPtrVal = C.getSVal(Buffer.Expression);
25430b57cec5SDimitry Andric
25440b57cec5SDimitry Andric // If the size is zero, there won't be any actual memory access, so
25455ffd83dbSDimitry Andric // just bind the return value to the buffer and return.
25465ffd83dbSDimitry Andric if (ZeroSize && !NonZeroSize) {
2547647cbc5dSDimitry Andric ZeroSize = ZeroSize->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);
25485ffd83dbSDimitry Andric C.addTransition(ZeroSize);
25490b57cec5SDimitry Andric return;
25500b57cec5SDimitry Andric }
25510b57cec5SDimitry Andric
25520b57cec5SDimitry Andric // Ensure the memory area is not null.
25530b57cec5SDimitry Andric // If it is NULL there will be a NULL pointer dereference.
25545ffd83dbSDimitry Andric State = checkNonNull(C, NonZeroSize, Buffer, BufferPtrVal);
25550b57cec5SDimitry Andric if (!State)
25560b57cec5SDimitry Andric return;
25570b57cec5SDimitry Andric
25585ffd83dbSDimitry Andric State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
25590b57cec5SDimitry Andric if (!State)
25600b57cec5SDimitry Andric return;
25610b57cec5SDimitry Andric
25620b57cec5SDimitry Andric // According to the values of the arguments, bind the value of the second
25630b57cec5SDimitry Andric // argument to the destination buffer and set string length, or just
25640b57cec5SDimitry Andric // invalidate the destination buffer.
25655ffd83dbSDimitry Andric if (!memsetAux(Buffer.Expression, C.getSVal(CharE.Expression),
25665ffd83dbSDimitry Andric Size.Expression, C, State))
25670b57cec5SDimitry Andric return;
25680b57cec5SDimitry Andric
2569647cbc5dSDimitry Andric State = State->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);
25700b57cec5SDimitry Andric C.addTransition(State);
25710b57cec5SDimitry Andric }
25720b57cec5SDimitry Andric
evalBzero(CheckerContext & C,const CallEvent & Call) const2573647cbc5dSDimitry Andric void CStringChecker::evalBzero(CheckerContext &C, const CallEvent &Call) const {
25740b57cec5SDimitry Andric CurrentFunctionDescription = "memory clearance function";
25750b57cec5SDimitry Andric
2576647cbc5dSDimitry Andric DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};
2577647cbc5dSDimitry Andric SizeArgExpr Size = {{Call.getArgExpr(1), 1}};
25780b57cec5SDimitry Andric SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
25790b57cec5SDimitry Andric
25800b57cec5SDimitry Andric ProgramStateRef State = C.getState();
25810b57cec5SDimitry Andric
25820b57cec5SDimitry Andric // See if the size argument is zero.
25835ffd83dbSDimitry Andric SVal SizeVal = C.getSVal(Size.Expression);
25845ffd83dbSDimitry Andric QualType SizeTy = Size.Expression->getType();
25850b57cec5SDimitry Andric
25860b57cec5SDimitry Andric ProgramStateRef StateZeroSize, StateNonZeroSize;
25870b57cec5SDimitry Andric std::tie(StateZeroSize, StateNonZeroSize) =
25880b57cec5SDimitry Andric assumeZero(C, State, SizeVal, SizeTy);
25890b57cec5SDimitry Andric
25900b57cec5SDimitry Andric // If the size is zero, there won't be any actual memory access,
25910b57cec5SDimitry Andric // In this case we just return.
25920b57cec5SDimitry Andric if (StateZeroSize && !StateNonZeroSize) {
25930b57cec5SDimitry Andric C.addTransition(StateZeroSize);
25940b57cec5SDimitry Andric return;
25950b57cec5SDimitry Andric }
25960b57cec5SDimitry Andric
25970b57cec5SDimitry Andric // Get the value of the memory area.
25985ffd83dbSDimitry Andric SVal MemVal = C.getSVal(Buffer.Expression);
25990b57cec5SDimitry Andric
26000b57cec5SDimitry Andric // Ensure the memory area is not null.
26010b57cec5SDimitry Andric // If it is NULL there will be a NULL pointer dereference.
26025ffd83dbSDimitry Andric State = checkNonNull(C, StateNonZeroSize, Buffer, MemVal);
26030b57cec5SDimitry Andric if (!State)
26040b57cec5SDimitry Andric return;
26050b57cec5SDimitry Andric
26065ffd83dbSDimitry Andric State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);
26070b57cec5SDimitry Andric if (!State)
26080b57cec5SDimitry Andric return;
26090b57cec5SDimitry Andric
26105ffd83dbSDimitry Andric if (!memsetAux(Buffer.Expression, Zero, Size.Expression, C, State))
26110b57cec5SDimitry Andric return;
26120b57cec5SDimitry Andric
26130b57cec5SDimitry Andric C.addTransition(State);
26140b57cec5SDimitry Andric }
26150b57cec5SDimitry Andric
evalSprintf(CheckerContext & C,const CallEvent & Call) const2616647cbc5dSDimitry Andric void CStringChecker::evalSprintf(CheckerContext &C,
2617647cbc5dSDimitry Andric const CallEvent &Call) const {
261806c3fb27SDimitry Andric CurrentFunctionDescription = "'sprintf'";
2619*0fca6ea1SDimitry Andric evalSprintfCommon(C, Call, /* IsBounded = */ false);
262006c3fb27SDimitry Andric }
262106c3fb27SDimitry Andric
evalSnprintf(CheckerContext & C,const CallEvent & Call) const2622647cbc5dSDimitry Andric void CStringChecker::evalSnprintf(CheckerContext &C,
2623647cbc5dSDimitry Andric const CallEvent &Call) const {
262406c3fb27SDimitry Andric CurrentFunctionDescription = "'snprintf'";
2625*0fca6ea1SDimitry Andric evalSprintfCommon(C, Call, /* IsBounded = */ true);
262606c3fb27SDimitry Andric }
262706c3fb27SDimitry Andric
evalSprintfCommon(CheckerContext & C,const CallEvent & Call,bool IsBounded) const2628647cbc5dSDimitry Andric void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallEvent &Call,
2629*0fca6ea1SDimitry Andric bool IsBounded) const {
263006c3fb27SDimitry Andric ProgramStateRef State = C.getState();
2631647cbc5dSDimitry Andric const auto *CE = cast<CallExpr>(Call.getOriginExpr());
2632647cbc5dSDimitry Andric DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};
263306c3fb27SDimitry Andric
2634647cbc5dSDimitry Andric const auto NumParams = Call.parameters().size();
2635*0fca6ea1SDimitry Andric if (CE->getNumArgs() < NumParams) {
2636*0fca6ea1SDimitry Andric // This is an invalid call, let's just ignore it.
2637*0fca6ea1SDimitry Andric return;
2638*0fca6ea1SDimitry Andric }
263906c3fb27SDimitry Andric
264006c3fb27SDimitry Andric const auto AllArguments =
264106c3fb27SDimitry Andric llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
264206c3fb27SDimitry Andric const auto VariadicArguments = drop_begin(enumerate(AllArguments), NumParams);
264306c3fb27SDimitry Andric
264406c3fb27SDimitry Andric for (const auto &[ArgIdx, ArgExpr] : VariadicArguments) {
264506c3fb27SDimitry Andric // We consider only string buffers
264606c3fb27SDimitry Andric if (const QualType type = ArgExpr->getType();
264706c3fb27SDimitry Andric !type->isAnyPointerType() ||
264806c3fb27SDimitry Andric !type->getPointeeType()->isAnyCharacterType())
264906c3fb27SDimitry Andric continue;
265006c3fb27SDimitry Andric SourceArgExpr Source = {{ArgExpr, unsigned(ArgIdx)}};
265106c3fb27SDimitry Andric
265206c3fb27SDimitry Andric // Ensure the buffers do not overlap.
265306c3fb27SDimitry Andric SizeArgExpr SrcExprAsSizeDummy = {
265406c3fb27SDimitry Andric {Source.Expression, Source.ArgumentIndex}};
265506c3fb27SDimitry Andric State = CheckOverlap(
265606c3fb27SDimitry Andric C, State,
2657647cbc5dSDimitry Andric (IsBounded ? SizeArgExpr{{Call.getArgExpr(1), 1}} : SrcExprAsSizeDummy),
265806c3fb27SDimitry Andric Dest, Source);
265906c3fb27SDimitry Andric if (!State)
266006c3fb27SDimitry Andric return;
266106c3fb27SDimitry Andric }
266206c3fb27SDimitry Andric
266306c3fb27SDimitry Andric C.addTransition(State);
266406c3fb27SDimitry Andric }
266506c3fb27SDimitry Andric
26660b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
26670b57cec5SDimitry Andric // The driver method, and other Checker callbacks.
26680b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
26690b57cec5SDimitry Andric
identifyCall(const CallEvent & Call,CheckerContext & C) const26700b57cec5SDimitry Andric CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
26710b57cec5SDimitry Andric CheckerContext &C) const {
26720b57cec5SDimitry Andric const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
26730b57cec5SDimitry Andric if (!CE)
26740b57cec5SDimitry Andric return nullptr;
26750b57cec5SDimitry Andric
26760b57cec5SDimitry Andric const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
26770b57cec5SDimitry Andric if (!FD)
26780b57cec5SDimitry Andric return nullptr;
26790b57cec5SDimitry Andric
2680349cc55cSDimitry Andric if (StdCopy.matches(Call))
26810b57cec5SDimitry Andric return &CStringChecker::evalStdCopy;
2682349cc55cSDimitry Andric if (StdCopyBackward.matches(Call))
26830b57cec5SDimitry Andric return &CStringChecker::evalStdCopyBackward;
26840b57cec5SDimitry Andric
26850b57cec5SDimitry Andric // Pro-actively check that argument types are safe to do arithmetic upon.
26860b57cec5SDimitry Andric // We do not want to crash if someone accidentally passes a structure
26870b57cec5SDimitry Andric // into, say, a C++ overload of any of these functions. We could not check
26880b57cec5SDimitry Andric // that for std::copy because they may have arguments of other types.
26890b57cec5SDimitry Andric for (auto I : CE->arguments()) {
26900b57cec5SDimitry Andric QualType T = I->getType();
26910b57cec5SDimitry Andric if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
26920b57cec5SDimitry Andric return nullptr;
26930b57cec5SDimitry Andric }
26940b57cec5SDimitry Andric
26950b57cec5SDimitry Andric const FnCheck *Callback = Callbacks.lookup(Call);
26960b57cec5SDimitry Andric if (Callback)
26970b57cec5SDimitry Andric return *Callback;
26980b57cec5SDimitry Andric
26990b57cec5SDimitry Andric return nullptr;
27000b57cec5SDimitry Andric }
27010b57cec5SDimitry Andric
evalCall(const CallEvent & Call,CheckerContext & C) const27020b57cec5SDimitry Andric bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
27030b57cec5SDimitry Andric FnCheck Callback = identifyCall(Call, C);
27040b57cec5SDimitry Andric
27050b57cec5SDimitry Andric // If the callee isn't a string function, let another checker handle it.
27060b57cec5SDimitry Andric if (!Callback)
27070b57cec5SDimitry Andric return false;
27080b57cec5SDimitry Andric
27090b57cec5SDimitry Andric // Check and evaluate the call.
2710647cbc5dSDimitry Andric assert(isa<CallExpr>(Call.getOriginExpr()));
2711647cbc5dSDimitry Andric Callback(this, C, Call);
27120b57cec5SDimitry Andric
27130b57cec5SDimitry Andric // If the evaluate call resulted in no change, chain to the next eval call
27140b57cec5SDimitry Andric // handler.
27150b57cec5SDimitry Andric // Note, the custom CString evaluation calls assume that basic safety
27160b57cec5SDimitry Andric // properties are held. However, if the user chooses to turn off some of these
27170b57cec5SDimitry Andric // checks, we ignore the issues and leave the call evaluation to a generic
27180b57cec5SDimitry Andric // handler.
27190b57cec5SDimitry Andric return C.isDifferent();
27200b57cec5SDimitry Andric }
27210b57cec5SDimitry Andric
checkPreStmt(const DeclStmt * DS,CheckerContext & C) const27220b57cec5SDimitry Andric void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
27230b57cec5SDimitry Andric // Record string length for char a[] = "abc";
27240b57cec5SDimitry Andric ProgramStateRef state = C.getState();
27250b57cec5SDimitry Andric
27260b57cec5SDimitry Andric for (const auto *I : DS->decls()) {
27270b57cec5SDimitry Andric const VarDecl *D = dyn_cast<VarDecl>(I);
27280b57cec5SDimitry Andric if (!D)
27290b57cec5SDimitry Andric continue;
27300b57cec5SDimitry Andric
27310b57cec5SDimitry Andric // FIXME: Handle array fields of structs.
27320b57cec5SDimitry Andric if (!D->getType()->isArrayType())
27330b57cec5SDimitry Andric continue;
27340b57cec5SDimitry Andric
27350b57cec5SDimitry Andric const Expr *Init = D->getInit();
27360b57cec5SDimitry Andric if (!Init)
27370b57cec5SDimitry Andric continue;
27380b57cec5SDimitry Andric if (!isa<StringLiteral>(Init))
27390b57cec5SDimitry Andric continue;
27400b57cec5SDimitry Andric
27410b57cec5SDimitry Andric Loc VarLoc = state->getLValue(D, C.getLocationContext());
27420b57cec5SDimitry Andric const MemRegion *MR = VarLoc.getAsRegion();
27430b57cec5SDimitry Andric if (!MR)
27440b57cec5SDimitry Andric continue;
27450b57cec5SDimitry Andric
27460b57cec5SDimitry Andric SVal StrVal = C.getSVal(Init);
27470b57cec5SDimitry Andric assert(StrVal.isValid() && "Initializer string is unknown or undefined");
27480b57cec5SDimitry Andric DefinedOrUnknownSVal strLength =
27490b57cec5SDimitry Andric getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();
27500b57cec5SDimitry Andric
27510b57cec5SDimitry Andric state = state->set<CStringLength>(MR, strLength);
27520b57cec5SDimitry Andric }
27530b57cec5SDimitry Andric
27540b57cec5SDimitry Andric C.addTransition(state);
27550b57cec5SDimitry Andric }
27560b57cec5SDimitry Andric
27570b57cec5SDimitry Andric ProgramStateRef
checkRegionChanges(ProgramStateRef state,const InvalidatedSymbols *,ArrayRef<const MemRegion * > ExplicitRegions,ArrayRef<const MemRegion * > Regions,const LocationContext * LCtx,const CallEvent * Call) const27580b57cec5SDimitry Andric CStringChecker::checkRegionChanges(ProgramStateRef state,
27590b57cec5SDimitry Andric const InvalidatedSymbols *,
27600b57cec5SDimitry Andric ArrayRef<const MemRegion *> ExplicitRegions,
27610b57cec5SDimitry Andric ArrayRef<const MemRegion *> Regions,
27620b57cec5SDimitry Andric const LocationContext *LCtx,
27630b57cec5SDimitry Andric const CallEvent *Call) const {
27640b57cec5SDimitry Andric CStringLengthTy Entries = state->get<CStringLength>();
27650b57cec5SDimitry Andric if (Entries.isEmpty())
27660b57cec5SDimitry Andric return state;
27670b57cec5SDimitry Andric
27680b57cec5SDimitry Andric llvm::SmallPtrSet<const MemRegion *, 8> Invalidated;
27690b57cec5SDimitry Andric llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
27700b57cec5SDimitry Andric
27710b57cec5SDimitry Andric // First build sets for the changed regions and their super-regions.
277206c3fb27SDimitry Andric for (const MemRegion *MR : Regions) {
27730b57cec5SDimitry Andric Invalidated.insert(MR);
27740b57cec5SDimitry Andric
27750b57cec5SDimitry Andric SuperRegions.insert(MR);
27760b57cec5SDimitry Andric while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) {
27770b57cec5SDimitry Andric MR = SR->getSuperRegion();
27780b57cec5SDimitry Andric SuperRegions.insert(MR);
27790b57cec5SDimitry Andric }
27800b57cec5SDimitry Andric }
27810b57cec5SDimitry Andric
27820b57cec5SDimitry Andric CStringLengthTy::Factory &F = state->get_context<CStringLength>();
27830b57cec5SDimitry Andric
27840b57cec5SDimitry Andric // Then loop over the entries in the current state.
278506c3fb27SDimitry Andric for (const MemRegion *MR : llvm::make_first_range(Entries)) {
27860b57cec5SDimitry Andric // Is this entry for a super-region of a changed region?
27870b57cec5SDimitry Andric if (SuperRegions.count(MR)) {
27880b57cec5SDimitry Andric Entries = F.remove(Entries, MR);
27890b57cec5SDimitry Andric continue;
27900b57cec5SDimitry Andric }
27910b57cec5SDimitry Andric
27920b57cec5SDimitry Andric // Is this entry for a sub-region of a changed region?
27930b57cec5SDimitry Andric const MemRegion *Super = MR;
27940b57cec5SDimitry Andric while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) {
27950b57cec5SDimitry Andric Super = SR->getSuperRegion();
27960b57cec5SDimitry Andric if (Invalidated.count(Super)) {
27970b57cec5SDimitry Andric Entries = F.remove(Entries, MR);
27980b57cec5SDimitry Andric break;
27990b57cec5SDimitry Andric }
28000b57cec5SDimitry Andric }
28010b57cec5SDimitry Andric }
28020b57cec5SDimitry Andric
28030b57cec5SDimitry Andric return state->set<CStringLength>(Entries);
28040b57cec5SDimitry Andric }
28050b57cec5SDimitry Andric
checkLiveSymbols(ProgramStateRef state,SymbolReaper & SR) const28060b57cec5SDimitry Andric void CStringChecker::checkLiveSymbols(ProgramStateRef state,
28070b57cec5SDimitry Andric SymbolReaper &SR) const {
28080b57cec5SDimitry Andric // Mark all symbols in our string length map as valid.
28090b57cec5SDimitry Andric CStringLengthTy Entries = state->get<CStringLength>();
28100b57cec5SDimitry Andric
281106c3fb27SDimitry Andric for (SVal Len : llvm::make_second_range(Entries)) {
281206c3fb27SDimitry Andric for (SymbolRef Sym : Len.symbols())
281306c3fb27SDimitry Andric SR.markInUse(Sym);
28140b57cec5SDimitry Andric }
28150b57cec5SDimitry Andric }
28160b57cec5SDimitry Andric
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const28170b57cec5SDimitry Andric void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
28180b57cec5SDimitry Andric CheckerContext &C) const {
28190b57cec5SDimitry Andric ProgramStateRef state = C.getState();
28200b57cec5SDimitry Andric CStringLengthTy Entries = state->get<CStringLength>();
28210b57cec5SDimitry Andric if (Entries.isEmpty())
28220b57cec5SDimitry Andric return;
28230b57cec5SDimitry Andric
28240b57cec5SDimitry Andric CStringLengthTy::Factory &F = state->get_context<CStringLength>();
282506c3fb27SDimitry Andric for (auto [Reg, Len] : Entries) {
28260b57cec5SDimitry Andric if (SymbolRef Sym = Len.getAsSymbol()) {
28270b57cec5SDimitry Andric if (SR.isDead(Sym))
282806c3fb27SDimitry Andric Entries = F.remove(Entries, Reg);
28290b57cec5SDimitry Andric }
28300b57cec5SDimitry Andric }
28310b57cec5SDimitry Andric
28320b57cec5SDimitry Andric state = state->set<CStringLength>(Entries);
28330b57cec5SDimitry Andric C.addTransition(state);
28340b57cec5SDimitry Andric }
28350b57cec5SDimitry Andric
registerCStringModeling(CheckerManager & Mgr)28360b57cec5SDimitry Andric void ento::registerCStringModeling(CheckerManager &Mgr) {
28370b57cec5SDimitry Andric Mgr.registerChecker<CStringChecker>();
28380b57cec5SDimitry Andric }
28390b57cec5SDimitry Andric
shouldRegisterCStringModeling(const CheckerManager & mgr)28405ffd83dbSDimitry Andric bool ento::shouldRegisterCStringModeling(const CheckerManager &mgr) {
28410b57cec5SDimitry Andric return true;
28420b57cec5SDimitry Andric }
28430b57cec5SDimitry Andric
28440b57cec5SDimitry Andric #define REGISTER_CHECKER(name) \
28450b57cec5SDimitry Andric void ento::register##name(CheckerManager &mgr) { \
28460b57cec5SDimitry Andric CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
28470b57cec5SDimitry Andric checker->Filter.Check##name = true; \
2848a7dea167SDimitry Andric checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \
28490b57cec5SDimitry Andric } \
28500b57cec5SDimitry Andric \
28515ffd83dbSDimitry Andric bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
28520b57cec5SDimitry Andric
28530b57cec5SDimitry Andric REGISTER_CHECKER(CStringNullArg)
28540b57cec5SDimitry Andric REGISTER_CHECKER(CStringOutOfBounds)
28550b57cec5SDimitry Andric REGISTER_CHECKER(CStringBufferOverlap)
28560b57cec5SDimitry Andric REGISTER_CHECKER(CStringNotNullTerm)
285781ad6265SDimitry Andric REGISTER_CHECKER(CStringUninitializedRead)
2858