2969 lines · cpp
1//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This defines CStringChecker, which is an assortment of checks on calls10// to functions in <string.h>.11//12//===----------------------------------------------------------------------===//13 14#include "InterCheckerAPI.h"15#include "clang/AST/OperationKinds.h"16#include "clang/Basic/CharInfo.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"20#include "clang/StaticAnalyzer/Core/Checker.h"21#include "clang/StaticAnalyzer/Core/CheckerManager.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"24#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"25#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"27#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"28#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"29#include "llvm/ADT/APSInt.h"30#include "llvm/ADT/STLExtras.h"31#include "llvm/ADT/StringExtras.h"32#include "llvm/Support/raw_ostream.h"33#include <functional>34#include <optional>35 36using namespace clang;37using namespace ento;38using namespace std::placeholders;39 40namespace {41struct AnyArgExpr {42 const Expr *Expression;43 unsigned ArgumentIndex;44};45struct SourceArgExpr : AnyArgExpr {};46struct DestinationArgExpr : AnyArgExpr {};47struct SizeArgExpr : AnyArgExpr {};48 49using ErrorMessage = SmallString<128>;50enum class AccessKind { write, read };51 52static ErrorMessage createOutOfBoundErrorMsg(StringRef FunctionDescription,53 AccessKind Access) {54 ErrorMessage Message;55 llvm::raw_svector_ostream Os(Message);56 57 // Function classification like: Memory copy function58 Os << toUppercase(FunctionDescription.front())59 << &FunctionDescription.data()[1];60 61 if (Access == AccessKind::write) {62 Os << " overflows the destination buffer";63 } else { // read access64 Os << " accesses out-of-bound array element";65 }66 67 return Message;68}69 70enum class ConcatFnKind { none = 0, strcat = 1, strlcat = 2 };71 72enum class CharKind { Regular = 0, Wide };73constexpr CharKind CK_Regular = CharKind::Regular;74constexpr CharKind CK_Wide = CharKind::Wide;75 76static QualType getCharPtrType(ASTContext &Ctx, CharKind CK) {77 return Ctx.getPointerType(CK == CharKind::Regular ? Ctx.CharTy78 : Ctx.WideCharTy);79}80 81class CStringChecker82 : public CheckerFamily<eval::Call, check::PreStmt<DeclStmt>,83 check::LiveSymbols, check::DeadSymbols,84 check::RegionChanges> {85 mutable const char *CurrentFunctionDescription = nullptr;86 87public:88 // FIXME: The bug types emitted by this checker family have confused garbage89 // in their Description and Category fields (e.g. `categories::UnixAPI` is90 // passed as the description in several cases and `uninitialized` is mistyped91 // as `unitialized`). This should be cleaned up.92 CheckerFrontendWithBugType NullArg{categories::UnixAPI};93 CheckerFrontendWithBugType OutOfBounds{"Out-of-bound array access"};94 CheckerFrontendWithBugType BufferOverlap{categories::UnixAPI,95 "Improper arguments"};96 CheckerFrontendWithBugType NotNullTerm{categories::UnixAPI};97 CheckerFrontendWithBugType UninitializedRead{98 "Accessing unitialized/garbage values"};99 100 StringRef getDebugTag() const override { return "MallocChecker"; }101 102 static void *getTag() { static int tag; return &tag; }103 104 bool evalCall(const CallEvent &Call, CheckerContext &C) const;105 void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;106 void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;107 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;108 109 ProgramStateRef110 checkRegionChanges(ProgramStateRef state,111 const InvalidatedSymbols *,112 ArrayRef<const MemRegion *> ExplicitRegions,113 ArrayRef<const MemRegion *> Regions,114 const LocationContext *LCtx,115 const CallEvent *Call) const;116 117 using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,118 const CallEvent &)>;119 120 CallDescriptionMap<FnCheck> Callbacks = {121 {{CDM::CLibraryMaybeHardened, {"memcpy"}, 3},122 std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Regular)},123 {{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3},124 std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, CK_Wide)},125 {{CDM::CLibraryMaybeHardened, {"mempcpy"}, 3},126 std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Regular)},127 {{CDM::CLibraryMaybeHardened, {"wmempcpy"}, 3},128 std::bind(&CStringChecker::evalMempcpy, _1, _2, _3, CK_Wide)},129 {{CDM::CLibrary, {"memcmp"}, 3},130 std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},131 {{CDM::CLibrary, {"wmemcmp"}, 3},132 std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Wide)},133 {{CDM::CLibraryMaybeHardened, {"memmove"}, 3},134 std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Regular)},135 {{CDM::CLibraryMaybeHardened, {"wmemmove"}, 3},136 std::bind(&CStringChecker::evalMemmove, _1, _2, _3, CK_Wide)},137 {{CDM::CLibraryMaybeHardened, {"memset"}, 3},138 &CStringChecker::evalMemset},139 {{CDM::CLibrary, {"explicit_memset"}, 3}, &CStringChecker::evalMemset},140 // FIXME: C23 introduces 'memset_explicit', maybe also model that141 {{CDM::CLibraryMaybeHardened, {"strcpy"}, 2},142 &CStringChecker::evalStrcpy},143 {{CDM::CLibraryMaybeHardened, {"strncpy"}, 3},144 &CStringChecker::evalStrncpy},145 {{CDM::CLibraryMaybeHardened, {"stpcpy"}, 2},146 &CStringChecker::evalStpcpy},147 {{CDM::CLibraryMaybeHardened, {"strlcpy"}, 3},148 &CStringChecker::evalStrlcpy},149 {{CDM::CLibraryMaybeHardened, {"strcat"}, 2},150 &CStringChecker::evalStrcat},151 {{CDM::CLibraryMaybeHardened, {"strncat"}, 3},152 &CStringChecker::evalStrncat},153 {{CDM::CLibraryMaybeHardened, {"strlcat"}, 3},154 &CStringChecker::evalStrlcat},155 {{CDM::CLibraryMaybeHardened, {"strlen"}, 1},156 &CStringChecker::evalstrLength},157 {{CDM::CLibrary, {"wcslen"}, 1}, &CStringChecker::evalstrLength},158 {{CDM::CLibraryMaybeHardened, {"strnlen"}, 2},159 &CStringChecker::evalstrnLength},160 {{CDM::CLibrary, {"wcsnlen"}, 2}, &CStringChecker::evalstrnLength},161 {{CDM::CLibrary, {"strcmp"}, 2}, &CStringChecker::evalStrcmp},162 {{CDM::CLibrary, {"strncmp"}, 3}, &CStringChecker::evalStrncmp},163 {{CDM::CLibrary, {"strcasecmp"}, 2}, &CStringChecker::evalStrcasecmp},164 {{CDM::CLibrary, {"strncasecmp"}, 3}, &CStringChecker::evalStrncasecmp},165 {{CDM::CLibrary, {"strsep"}, 2}, &CStringChecker::evalStrsep},166 {{CDM::CLibrary, {"strxfrm"}, 3}, &CStringChecker::evalStrxfrm},167 {{CDM::CLibrary, {"bcopy"}, 3}, &CStringChecker::evalBcopy},168 {{CDM::CLibrary, {"bcmp"}, 3},169 std::bind(&CStringChecker::evalMemcmp, _1, _2, _3, CK_Regular)},170 {{CDM::CLibrary, {"bzero"}, 2}, &CStringChecker::evalBzero},171 {{CDM::CLibraryMaybeHardened, {"explicit_bzero"}, 2},172 &CStringChecker::evalBzero},173 174 // When recognizing calls to the following variadic functions, we accept175 // any number of arguments in the call (std::nullopt = accept any176 // number), but check that in the declaration there are 2 and 3177 // parameters respectively. (Note that the parameter count does not178 // include the "...". Calls where the number of arguments is too small179 // will be discarded by the callback.)180 {{CDM::CLibraryMaybeHardened, {"sprintf"}, std::nullopt, 2},181 &CStringChecker::evalSprintf},182 {{CDM::CLibraryMaybeHardened, {"snprintf"}, std::nullopt, 3},183 &CStringChecker::evalSnprintf},184 };185 186 // These require a bit of special handling.187 CallDescription StdCopy{CDM::SimpleFunc, {"std", "copy"}, 3},188 StdCopyBackward{CDM::SimpleFunc, {"std", "copy_backward"}, 3};189 190 FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;191 void evalMemcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;192 void evalMempcpy(CheckerContext &C, const CallEvent &Call, CharKind CK) const;193 void evalMemmove(CheckerContext &C, const CallEvent &Call, CharKind CK) const;194 void evalBcopy(CheckerContext &C, const CallEvent &Call) const;195 void evalCopyCommon(CheckerContext &C, const CallEvent &Call,196 ProgramStateRef state, SizeArgExpr Size,197 DestinationArgExpr Dest, SourceArgExpr Source,198 bool Restricted, bool IsMempcpy, CharKind CK) const;199 200 void evalMemcmp(CheckerContext &C, const CallEvent &Call, CharKind CK) const;201 202 void evalstrLength(CheckerContext &C, const CallEvent &Call) const;203 void evalstrnLength(CheckerContext &C, const CallEvent &Call) const;204 void evalstrLengthCommon(CheckerContext &C, const CallEvent &Call,205 bool IsStrnlen = false) const;206 207 void evalStrcpy(CheckerContext &C, const CallEvent &Call) const;208 void evalStrncpy(CheckerContext &C, const CallEvent &Call) const;209 void evalStpcpy(CheckerContext &C, const CallEvent &Call) const;210 void evalStrlcpy(CheckerContext &C, const CallEvent &Call) const;211 void evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,212 bool ReturnEnd, bool IsBounded, ConcatFnKind appendK,213 bool returnPtr = true) const;214 215 void evalStrxfrm(CheckerContext &C, const CallEvent &Call) const;216 217 void evalStrcat(CheckerContext &C, const CallEvent &Call) const;218 void evalStrncat(CheckerContext &C, const CallEvent &Call) const;219 void evalStrlcat(CheckerContext &C, const CallEvent &Call) const;220 221 void evalStrcmp(CheckerContext &C, const CallEvent &Call) const;222 void evalStrncmp(CheckerContext &C, const CallEvent &Call) const;223 void evalStrcasecmp(CheckerContext &C, const CallEvent &Call) const;224 void evalStrncasecmp(CheckerContext &C, const CallEvent &Call) const;225 void evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,226 bool IsBounded = false, bool IgnoreCase = false) const;227 228 void evalStrsep(CheckerContext &C, const CallEvent &Call) const;229 230 void evalStdCopy(CheckerContext &C, const CallEvent &Call) const;231 void evalStdCopyBackward(CheckerContext &C, const CallEvent &Call) const;232 void evalStdCopyCommon(CheckerContext &C, const CallEvent &Call) const;233 void evalMemset(CheckerContext &C, const CallEvent &Call) const;234 void evalBzero(CheckerContext &C, const CallEvent &Call) const;235 236 void evalSprintf(CheckerContext &C, const CallEvent &Call) const;237 void evalSnprintf(CheckerContext &C, const CallEvent &Call) const;238 void evalSprintfCommon(CheckerContext &C, const CallEvent &Call,239 bool IsBounded) const;240 241 // Utility methods242 std::pair<ProgramStateRef , ProgramStateRef >243 static assumeZero(CheckerContext &C,244 ProgramStateRef state, SVal V, QualType Ty);245 246 static ProgramStateRef setCStringLength(ProgramStateRef state,247 const MemRegion *MR,248 SVal strLength);249 static SVal getCStringLengthForRegion(CheckerContext &C,250 ProgramStateRef &state,251 const Expr *Ex,252 const MemRegion *MR,253 bool hypothetical);254 static const StringLiteral *getStringLiteralFromRegion(const MemRegion *MR);255 256 SVal getCStringLength(CheckerContext &C,257 ProgramStateRef &state,258 const Expr *Ex,259 SVal Buf,260 bool hypothetical = false) const;261 262 const StringLiteral *getCStringLiteral(CheckerContext &C,263 ProgramStateRef &state,264 const Expr *expr,265 SVal val) const;266 267 /// Invalidate the destination buffer determined by characters copied.268 static ProgramStateRef269 invalidateDestinationBufferBySize(CheckerContext &C, ProgramStateRef S,270 const Expr *BufE, ConstCFGElementRef Elem,271 SVal BufV, SVal SizeV, QualType SizeTy);272 273 /// Operation never overflows, do not invalidate the super region.274 static ProgramStateRef invalidateDestinationBufferNeverOverflows(275 CheckerContext &C, ProgramStateRef S, ConstCFGElementRef Elem, SVal BufV);276 277 /// We do not know whether the operation can overflow (e.g. size is unknown),278 /// invalidate the super region and escape related pointers.279 static ProgramStateRef invalidateDestinationBufferAlwaysEscapeSuperRegion(280 CheckerContext &C, ProgramStateRef S, ConstCFGElementRef Elem, SVal BufV);281 282 /// Invalidate the source buffer for escaping pointers.283 static ProgramStateRef invalidateSourceBuffer(CheckerContext &C,284 ProgramStateRef S,285 ConstCFGElementRef Elem,286 SVal BufV);287 288 /// @param InvalidationTraitOperations Determine how to invlidate the289 /// MemRegion by setting the invalidation traits. Return true to cause pointer290 /// escape, or false otherwise.291 static ProgramStateRef invalidateBufferAux(292 CheckerContext &C, ProgramStateRef State, ConstCFGElementRef Elem, SVal V,293 llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,294 const MemRegion *)>295 InvalidationTraitOperations);296 297 static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,298 const MemRegion *MR);299 300 static bool memsetAux(const Expr *DstBuffer, ConstCFGElementRef Elem,301 SVal CharE, const Expr *Size, CheckerContext &C,302 ProgramStateRef &State);303 304 // Re-usable checks305 ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef State,306 AnyArgExpr Arg, SVal l) const;307 // Check whether the origin region behind \p Element (like the actual array308 // region \p Element is from) is initialized.309 ProgramStateRef checkInit(CheckerContext &C, ProgramStateRef state,310 AnyArgExpr Buffer, SVal Element, SVal Size) const;311 ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,312 AnyArgExpr Buffer, SVal Element,313 AccessKind Access,314 CharKind CK = CharKind::Regular) const;315 ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,316 AnyArgExpr Buffer, SizeArgExpr Size,317 AccessKind Access,318 CharKind CK = CharKind::Regular) const;319 ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,320 SizeArgExpr Size, AnyArgExpr First,321 AnyArgExpr Second,322 CharKind CK = CharKind::Regular) const;323 void emitOverlapBug(CheckerContext &C,324 ProgramStateRef state,325 const Stmt *First,326 const Stmt *Second) const;327 328 void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S,329 StringRef WarningMsg) const;330 void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State,331 const Stmt *S, StringRef WarningMsg) const;332 void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,333 const Stmt *S, StringRef WarningMsg) const;334 void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State,335 const Expr *E, const MemRegion *R,336 StringRef Msg) const;337 ProgramStateRef checkAdditionOverflow(CheckerContext &C,338 ProgramStateRef state,339 NonLoc left,340 NonLoc right) const;341 342 // Return true if the destination buffer of the copy function may be in bound.343 // Expects SVal of Size to be positive and unsigned.344 // Expects SVal of FirstBuf to be a FieldRegion.345 static bool isFirstBufInBound(CheckerContext &C, ProgramStateRef State,346 SVal BufVal, QualType BufTy, SVal LengthVal,347 QualType LengthTy);348};349 350} //end anonymous namespace351 352REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)353 354//===----------------------------------------------------------------------===//355// Individual checks and utility methods.356//===----------------------------------------------------------------------===//357 358std::pair<ProgramStateRef, ProgramStateRef>359CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef State, SVal V,360 QualType Ty) {361 std::optional<DefinedSVal> val = V.getAs<DefinedSVal>();362 if (!val)363 return std::pair<ProgramStateRef, ProgramStateRef>(State, State);364 365 SValBuilder &svalBuilder = C.getSValBuilder();366 DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty);367 return State->assume(svalBuilder.evalEQ(State, *val, zero));368}369 370ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,371 ProgramStateRef State,372 AnyArgExpr Arg, SVal l) const {373 // If a previous check has failed, propagate the failure.374 if (!State)375 return nullptr;376 377 ProgramStateRef stateNull, stateNonNull;378 std::tie(stateNull, stateNonNull) =379 assumeZero(C, State, l, Arg.Expression->getType());380 381 if (stateNull && !stateNonNull) {382 if (NullArg.isEnabled()) {383 SmallString<80> buf;384 llvm::raw_svector_ostream OS(buf);385 assert(CurrentFunctionDescription);386 OS << "Null pointer passed as " << (Arg.ArgumentIndex + 1)387 << llvm::getOrdinalSuffix(Arg.ArgumentIndex + 1) << " argument to "388 << CurrentFunctionDescription;389 390 emitNullArgBug(C, stateNull, Arg.Expression, OS.str());391 }392 return nullptr;393 }394 395 // From here on, assume that the value is non-null.396 assert(stateNonNull);397 return stateNonNull;398}399 400static std::optional<NonLoc> getIndex(ProgramStateRef State,401 const ElementRegion *ER, CharKind CK) {402 SValBuilder &SVB = State->getStateManager().getSValBuilder();403 ASTContext &Ctx = SVB.getContext();404 405 if (CK == CharKind::Regular) {406 if (ER->getValueType() != Ctx.CharTy)407 return {};408 return ER->getIndex();409 }410 411 if (ER->getValueType() != Ctx.WideCharTy)412 return {};413 414 QualType SizeTy = Ctx.getSizeType();415 NonLoc WideSize =416 SVB.makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),417 SizeTy)418 .castAs<NonLoc>();419 SVal Offset =420 SVB.evalBinOpNN(State, BO_Mul, ER->getIndex(), WideSize, SizeTy);421 if (Offset.isUnknown())422 return {};423 return Offset.castAs<NonLoc>();424}425 426// Basically 1 -> 1st, 12 -> 12th, etc.427static void printIdxWithOrdinalSuffix(llvm::raw_ostream &Os, unsigned Idx) {428 Os << Idx << llvm::getOrdinalSuffix(Idx);429}430 431ProgramStateRef CStringChecker::checkInit(CheckerContext &C,432 ProgramStateRef State,433 AnyArgExpr Buffer, SVal Element,434 SVal Size) const {435 436 // If a previous check has failed, propagate the failure.437 if (!State)438 return nullptr;439 440 const MemRegion *R = Element.getAsRegion();441 const auto *ER = dyn_cast_or_null<ElementRegion>(R);442 if (!ER)443 return State;444 445 const auto *SuperR = ER->getSuperRegion()->getAs<TypedValueRegion>();446 if (!SuperR)447 return State;448 449 // FIXME: We ought to able to check objects as well. Maybe450 // UninitializedObjectChecker could help?451 if (!SuperR->getValueType()->isArrayType())452 return State;453 454 SValBuilder &SVB = C.getSValBuilder();455 ASTContext &Ctx = SVB.getContext();456 457 const QualType ElemTy = Ctx.getBaseElementType(SuperR->getValueType());458 const NonLoc Zero = SVB.makeZeroArrayIndex();459 460 std::optional<Loc> FirstElementVal =461 State->getLValue(ElemTy, Zero, loc::MemRegionVal(SuperR)).getAs<Loc>();462 if (!FirstElementVal)463 return State;464 465 // Ensure that we wouldn't read uninitialized value.466 if (UninitializedRead.isEnabled() &&467 State->getSVal(*FirstElementVal).isUndef()) {468 llvm::SmallString<258> Buf;469 llvm::raw_svector_ostream OS(Buf);470 OS << "The first element of the ";471 printIdxWithOrdinalSuffix(OS, Buffer.ArgumentIndex + 1);472 OS << " argument is undefined";473 emitUninitializedReadBug(C, State, Buffer.Expression,474 FirstElementVal->getAsRegion(), OS.str());475 return nullptr;476 }477 478 // We won't check whether the entire region is fully initialized -- lets just479 // check that the first and the last element is. So, onto checking the last480 // element:481 const QualType IdxTy = SVB.getArrayIndexType();482 483 NonLoc ElemSize =484 SVB.makeIntVal(Ctx.getTypeSizeInChars(ElemTy).getQuantity(), IdxTy)485 .castAs<NonLoc>();486 487 // FIXME: Check that the size arg to the cstring function is divisible by488 // size of the actual element type?489 490 // The type of the argument to the cstring function is either char or wchar,491 // but thats not the type of the original array (or memory region).492 // Suppose the following:493 // int t[5];494 // memcpy(dst, t, sizeof(t) / sizeof(t[0]));495 // When checking whether t is fully initialized, we see it as char array of496 // size sizeof(int)*5. If we check the last element as a character, we read497 // the last byte of an integer, which will be undefined. But just because498 // that value is undefined, it doesn't mean that the element is uninitialized!499 // For this reason, we need to retrieve the actual last element with the500 // correct type.501 502 // Divide the size argument to the cstring function by the actual element503 // type. This value will be size of the array, or the index to the504 // past-the-end element.505 std::optional<NonLoc> Offset =506 SVB.evalBinOpNN(State, clang::BO_Div, Size.castAs<NonLoc>(), ElemSize,507 IdxTy)508 .getAs<NonLoc>();509 510 // Retrieve the index of the last element.511 const NonLoc One = SVB.makeIntVal(1, IdxTy).castAs<NonLoc>();512 SVal LastIdx = SVB.evalBinOpNN(State, BO_Sub, *Offset, One, IdxTy);513 514 if (!Offset)515 return State;516 517 SVal LastElementVal =518 State->getLValue(ElemTy, LastIdx, loc::MemRegionVal(SuperR));519 if (!isa<Loc>(LastElementVal))520 return State;521 522 if (UninitializedRead.isEnabled() &&523 State->getSVal(LastElementVal.castAs<Loc>()).isUndef()) {524 const llvm::APSInt *IdxInt = LastIdx.getAsInteger();525 // If we can't get emit a sensible last element index, just bail out --526 // prefer to emit nothing in favour of emitting garbage quality reports.527 if (!IdxInt) {528 C.addSink();529 return nullptr;530 }531 llvm::SmallString<258> Buf;532 llvm::raw_svector_ostream OS(Buf);533 OS << "The last accessed element (at index ";534 OS << IdxInt->getExtValue();535 OS << ") in the ";536 printIdxWithOrdinalSuffix(OS, Buffer.ArgumentIndex + 1);537 OS << " argument is undefined";538 emitUninitializedReadBug(C, State, Buffer.Expression,539 LastElementVal.getAsRegion(), OS.str());540 return nullptr;541 }542 return State;543}544// FIXME: The root of this logic was copied from the old checker545// alpha.security.ArrayBound (which is removed within this commit).546// It should be refactored to use the different, more sophisticated bounds547// checking logic used by the new checker ``security.ArrayBound``.548ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,549 ProgramStateRef state,550 AnyArgExpr Buffer, SVal Element,551 AccessKind Access,552 CharKind CK) const {553 554 // If a previous check has failed, propagate the failure.555 if (!state)556 return nullptr;557 558 // Check for out of bound array element access.559 const MemRegion *R = Element.getAsRegion();560 if (!R)561 return state;562 563 const auto *ER = dyn_cast<ElementRegion>(R);564 if (!ER)565 return state;566 567 // Get the index of the accessed element.568 std::optional<NonLoc> Idx = getIndex(state, ER, CK);569 if (!Idx)570 return state;571 572 // Get the size of the array.573 const auto *superReg = cast<SubRegion>(ER->getSuperRegion());574 DefinedOrUnknownSVal Size =575 getDynamicExtent(state, superReg, C.getSValBuilder());576 577 auto [StInBound, StOutBound] = state->assumeInBoundDual(*Idx, Size);578 if (StOutBound && !StInBound) {579 if (!OutOfBounds.isEnabled())580 return nullptr;581 582 ErrorMessage Message =583 createOutOfBoundErrorMsg(CurrentFunctionDescription, Access);584 emitOutOfBoundsBug(C, StOutBound, Buffer.Expression, Message);585 return nullptr;586 }587 588 // Array bound check succeeded. From this point forward the array bound589 // should always succeed.590 return StInBound;591}592 593ProgramStateRef594CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,595 AnyArgExpr Buffer, SizeArgExpr Size,596 AccessKind Access, CharKind CK) const {597 // If a previous check has failed, propagate the failure.598 if (!State)599 return nullptr;600 601 SValBuilder &svalBuilder = C.getSValBuilder();602 ASTContext &Ctx = svalBuilder.getContext();603 604 QualType SizeTy = Size.Expression->getType();605 QualType PtrTy = getCharPtrType(Ctx, CK);606 607 // Check that the first buffer is non-null.608 SVal BufVal = C.getSVal(Buffer.Expression);609 State = checkNonNull(C, State, Buffer, BufVal);610 if (!State)611 return nullptr;612 613 // If out-of-bounds checking is turned off, skip the rest.614 if (!OutOfBounds.isEnabled())615 return State;616 617 SVal BufStart =618 svalBuilder.evalCast(BufVal, PtrTy, Buffer.Expression->getType());619 620 // Check if the first byte of the buffer is accessible.621 State = CheckLocation(C, State, Buffer, BufStart, Access, CK);622 623 if (!State)624 return nullptr;625 626 // Get the access length and make sure it is known.627 // FIXME: This assumes the caller has already checked that the access length628 // is positive. And that it's unsigned.629 SVal LengthVal = C.getSVal(Size.Expression);630 std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();631 if (!Length)632 return State;633 634 // Compute the offset of the last element to be accessed: size-1.635 NonLoc One = svalBuilder.makeIntVal(1, SizeTy).castAs<NonLoc>();636 SVal Offset = svalBuilder.evalBinOpNN(State, BO_Sub, *Length, One, SizeTy);637 if (Offset.isUnknown())638 return nullptr;639 NonLoc LastOffset = Offset.castAs<NonLoc>();640 641 // Check that the first buffer is sufficiently long.642 if (std::optional<Loc> BufLoc = BufStart.getAs<Loc>()) {643 644 SVal BufEnd =645 svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);646 State = CheckLocation(C, State, Buffer, BufEnd, Access, CK);647 if (Access == AccessKind::read)648 State = checkInit(C, State, Buffer, BufEnd, *Length);649 650 // If the buffer isn't large enough, abort.651 if (!State)652 return nullptr;653 }654 655 // Large enough or not, return this state!656 return State;657}658 659ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,660 ProgramStateRef state,661 SizeArgExpr Size, AnyArgExpr First,662 AnyArgExpr Second,663 CharKind CK) const {664 if (!BufferOverlap.isEnabled())665 return state;666 667 // Do a simple check for overlap: if the two arguments are from the same668 // buffer, see if the end of the first is greater than the start of the second669 // or vice versa.670 671 // If a previous check has failed, propagate the failure.672 if (!state)673 return nullptr;674 675 ProgramStateRef stateTrue, stateFalse;676 677 if (!First.Expression->getType()->isAnyPointerType() ||678 !Second.Expression->getType()->isAnyPointerType())679 return state;680 681 // Assume different address spaces cannot overlap.682 if (First.Expression->getType()->getPointeeType().getAddressSpace() !=683 Second.Expression->getType()->getPointeeType().getAddressSpace())684 return state;685 686 // Get the buffer values and make sure they're known locations.687 const LocationContext *LCtx = C.getLocationContext();688 SVal firstVal = state->getSVal(First.Expression, LCtx);689 SVal secondVal = state->getSVal(Second.Expression, LCtx);690 691 std::optional<Loc> firstLoc = firstVal.getAs<Loc>();692 if (!firstLoc)693 return state;694 695 std::optional<Loc> secondLoc = secondVal.getAs<Loc>();696 if (!secondLoc)697 return state;698 699 // Are the two values the same?700 SValBuilder &svalBuilder = C.getSValBuilder();701 std::tie(stateTrue, stateFalse) =702 state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));703 704 if (stateTrue && !stateFalse) {705 // If the values are known to be equal, that's automatically an overlap.706 emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);707 return nullptr;708 }709 710 // assume the two expressions are not equal.711 assert(stateFalse);712 state = stateFalse;713 714 // Which value comes first?715 QualType cmpTy = svalBuilder.getConditionType();716 SVal reverse =717 svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy);718 std::optional<DefinedOrUnknownSVal> reverseTest =719 reverse.getAs<DefinedOrUnknownSVal>();720 if (!reverseTest)721 return state;722 723 std::tie(stateTrue, stateFalse) = state->assume(*reverseTest);724 if (stateTrue) {725 if (stateFalse) {726 // If we don't know which one comes first, we can't perform this test.727 return state;728 } else {729 // Switch the values so that firstVal is before secondVal.730 std::swap(firstLoc, secondLoc);731 732 // Switch the Exprs as well, so that they still correspond.733 std::swap(First, Second);734 }735 }736 737 // Get the length, and make sure it too is known.738 SVal LengthVal = state->getSVal(Size.Expression, LCtx);739 std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();740 if (!Length)741 return state;742 743 // Convert the first buffer's start address to char*.744 // Bail out if the cast fails.745 ASTContext &Ctx = svalBuilder.getContext();746 QualType CharPtrTy = getCharPtrType(Ctx, CK);747 SVal FirstStart =748 svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());749 std::optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();750 if (!FirstStartLoc)751 return state;752 753 // Compute the end of the first buffer. Bail out if THAT fails.754 SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, *FirstStartLoc,755 *Length, CharPtrTy);756 std::optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();757 if (!FirstEndLoc)758 return state;759 760 // Is the end of the first buffer past the start of the second buffer?761 SVal Overlap =762 svalBuilder.evalBinOpLL(state, BO_GT, *FirstEndLoc, *secondLoc, cmpTy);763 std::optional<DefinedOrUnknownSVal> OverlapTest =764 Overlap.getAs<DefinedOrUnknownSVal>();765 if (!OverlapTest)766 return state;767 768 std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);769 770 if (stateTrue && !stateFalse) {771 // Overlap!772 emitOverlapBug(C, stateTrue, First.Expression, Second.Expression);773 return nullptr;774 }775 776 // assume the two expressions don't overlap.777 assert(stateFalse);778 return stateFalse;779}780 781void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,782 const Stmt *First, const Stmt *Second) const {783 ExplodedNode *N = C.generateErrorNode(state);784 if (!N)785 return;786 787 // Generate a report for this bug.788 auto report = std::make_unique<PathSensitiveBugReport>(789 BufferOverlap, "Arguments must not be overlapping buffers", N);790 report->addRange(First->getSourceRange());791 report->addRange(Second->getSourceRange());792 793 C.emitReport(std::move(report));794}795 796void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,797 const Stmt *S, StringRef WarningMsg) const {798 if (ExplodedNode *N = C.generateErrorNode(State)) {799 auto Report =800 std::make_unique<PathSensitiveBugReport>(NullArg, WarningMsg, N);801 Report->addRange(S->getSourceRange());802 if (const auto *Ex = dyn_cast<Expr>(S))803 bugreporter::trackExpressionValue(N, Ex, *Report);804 C.emitReport(std::move(Report));805 }806}807 808void CStringChecker::emitUninitializedReadBug(CheckerContext &C,809 ProgramStateRef State,810 const Expr *E, const MemRegion *R,811 StringRef Msg) const {812 if (ExplodedNode *N = C.generateErrorNode(State)) {813 auto Report =814 std::make_unique<PathSensitiveBugReport>(UninitializedRead, Msg, N);815 Report->addNote("Other elements might also be undefined",816 Report->getLocation());817 Report->addRange(E->getSourceRange());818 bugreporter::trackExpressionValue(N, E, *Report);819 Report->addVisitor<NoStoreFuncVisitor>(R->castAs<SubRegion>());820 C.emitReport(std::move(Report));821 }822}823 824void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,825 ProgramStateRef State, const Stmt *S,826 StringRef WarningMsg) const {827 if (ExplodedNode *N = C.generateErrorNode(State)) {828 // FIXME: It would be nice to eventually make this diagnostic more clear,829 // e.g., by referencing the original declaration or by saying *why* this830 // reference is outside the range.831 auto Report =832 std::make_unique<PathSensitiveBugReport>(OutOfBounds, WarningMsg, N);833 Report->addRange(S->getSourceRange());834 C.emitReport(std::move(Report));835 }836}837 838void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,839 const Stmt *S,840 StringRef WarningMsg) const {841 if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {842 auto Report =843 std::make_unique<PathSensitiveBugReport>(NotNullTerm, WarningMsg, N);844 845 Report->addRange(S->getSourceRange());846 C.emitReport(std::move(Report));847 }848}849 850ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,851 ProgramStateRef state,852 NonLoc left,853 NonLoc right) const {854 // If out-of-bounds checking is turned off, skip the rest.855 if (!OutOfBounds.isEnabled())856 return state;857 858 // If a previous check has failed, propagate the failure.859 if (!state)860 return nullptr;861 862 SValBuilder &svalBuilder = C.getSValBuilder();863 BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();864 865 QualType sizeTy = svalBuilder.getContext().getSizeType();866 const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);867 NonLoc maxVal = svalBuilder.makeIntVal(maxValInt);868 869 SVal maxMinusRight;870 if (isa<nonloc::ConcreteInt>(right)) {871 maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right,872 sizeTy);873 } else {874 // Try switching the operands. (The order of these two assignments is875 // important!)876 maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,877 sizeTy);878 left = right;879 }880 881 if (std::optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {882 QualType cmpTy = svalBuilder.getConditionType();883 // If left > max - right, we have an overflow.884 SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,885 *maxMinusRightNL, cmpTy);886 887 auto [StateOverflow, StateOkay] =888 state->assume(willOverflow.castAs<DefinedOrUnknownSVal>());889 890 if (StateOverflow && !StateOkay) {891 // On this path the analyzer is convinced that the addition of these two892 // values would overflow `size_t` which must be caused by the inaccuracy893 // of our modeling because this method is called in situations where the894 // summands are size/length values which are much less than SIZE_MAX. To895 // avoid false positives let's just sink this invalid path.896 C.addSink(StateOverflow);897 return nullptr;898 }899 900 // From now on, assume an overflow didn't occur.901 assert(StateOkay);902 state = StateOkay;903 }904 905 return state;906}907 908ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,909 const MemRegion *MR,910 SVal strLength) {911 assert(!strLength.isUndef() && "Attempt to set an undefined string length");912 913 MR = MR->StripCasts();914 915 switch (MR->getKind()) {916 case MemRegion::StringRegionKind:917 // FIXME: This can happen if we strcpy() into a string region. This is918 // undefined [C99 6.4.5p6], but we should still warn about it.919 return state;920 921 case MemRegion::SymbolicRegionKind:922 case MemRegion::AllocaRegionKind:923 case MemRegion::NonParamVarRegionKind:924 case MemRegion::ParamVarRegionKind:925 case MemRegion::FieldRegionKind:926 case MemRegion::ObjCIvarRegionKind:927 // These are the types we can currently track string lengths for.928 break;929 930 case MemRegion::ElementRegionKind:931 // FIXME: Handle element regions by upper-bounding the parent region's932 // string length.933 return state;934 935 default:936 // Other regions (mostly non-data) can't have a reliable C string length.937 // For now, just ignore the change.938 // FIXME: These are rare but not impossible. We should output some kind of939 // warning for things like strcpy((char[]){'a', 0}, "b");940 return state;941 }942 943 if (strLength.isUnknown())944 return state->remove<CStringLength>(MR);945 946 return state->set<CStringLength>(MR, strLength);947}948 949SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,950 ProgramStateRef &state,951 const Expr *Ex,952 const MemRegion *MR,953 bool hypothetical) {954 if (!hypothetical) {955 // If there's a recorded length, go ahead and return it.956 const SVal *Recorded = state->get<CStringLength>(MR);957 if (Recorded)958 return *Recorded;959 }960 961 // Otherwise, get a new symbol and update the state.962 SValBuilder &svalBuilder = C.getSValBuilder();963 QualType sizeTy = svalBuilder.getContext().getSizeType();964 SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(),965 MR, Ex, sizeTy,966 C.getLocationContext(),967 C.blockCount());968 969 if (!hypothetical) {970 if (std::optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {971 // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4972 BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();973 const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);974 llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4);975 std::optional<APSIntPtr> maxLengthInt =976 BVF.evalAPSInt(BO_Div, maxValInt, fourInt);977 NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt);978 SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, maxLength,979 svalBuilder.getConditionType());980 state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true);981 }982 state = state->set<CStringLength>(MR, strLength);983 }984 985 return strLength;986}987 988const StringLiteral *989CStringChecker::getStringLiteralFromRegion(const MemRegion *MR) {990 switch (MR->getKind()) {991 case MemRegion::StringRegionKind:992 return cast<StringRegion>(MR)->getStringLiteral();993 case MemRegion::NonParamVarRegionKind:994 if (const VarDecl *Decl = cast<NonParamVarRegion>(MR)->getDecl();995 Decl->getType().isConstQualified() && Decl->hasGlobalStorage())996 return dyn_cast_or_null<StringLiteral>(Decl->getInit());997 return nullptr;998 default:999 return nullptr;1000 }1001}1002 1003SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,1004 const Expr *Ex, SVal Buf,1005 bool hypothetical) const {1006 const MemRegion *MR = Buf.getAsRegion();1007 if (!MR) {1008 // If we can't get a region, see if it's something we /know/ isn't a1009 // C string. In the context of locations, the only time we can issue such1010 // a warning is for labels.1011 if (std::optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {1012 if (NotNullTerm.isEnabled()) {1013 SmallString<120> buf;1014 llvm::raw_svector_ostream os(buf);1015 assert(CurrentFunctionDescription);1016 os << "Argument to " << CurrentFunctionDescription1017 << " is the address of the label '" << Label->getLabel()->getName()1018 << "', which is not a null-terminated string";1019 1020 emitNotCStringBug(C, state, Ex, os.str());1021 }1022 return UndefinedVal();1023 }1024 1025 // If it's not a region and not a label, give up.1026 return UnknownVal();1027 }1028 1029 // If we have a region, strip casts from it and see if we can figure out1030 // its length. For anything we can't figure out, just return UnknownVal.1031 MR = MR->StripCasts();1032 1033 if (const StringLiteral *StrLit = getStringLiteralFromRegion(MR)) {1034 // If we have a global constant with a string literal initializer,1035 // compute the initializer's length.1036 // Modifying the contents of string regions is undefined [C99 6.4.5p6],1037 // so we can assume that the byte length is the correct C string length.1038 // FIXME: Embedded null characters are not handled.1039 SValBuilder &SVB = C.getSValBuilder();1040 return SVB.makeIntVal(StrLit->getLength(), SVB.getContext().getSizeType());1041 }1042 1043 switch (MR->getKind()) {1044 case MemRegion::StringRegionKind:1045 case MemRegion::NonParamVarRegionKind:1046 case MemRegion::SymbolicRegionKind:1047 case MemRegion::AllocaRegionKind:1048 case MemRegion::ParamVarRegionKind:1049 case MemRegion::FieldRegionKind:1050 case MemRegion::ObjCIvarRegionKind:1051 return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);1052 case MemRegion::CompoundLiteralRegionKind:1053 // FIXME: Can we track this? Is it necessary?1054 return UnknownVal();1055 case MemRegion::ElementRegionKind: {1056 // If an offset into the string literal is used, use the original length1057 // minus the offset.1058 // FIXME: Embedded null characters are not handled.1059 const ElementRegion *ER = cast<ElementRegion>(MR);1060 const SubRegion *SuperReg =1061 cast<SubRegion>(ER->getSuperRegion()->StripCasts());1062 const StringLiteral *StrLit = getStringLiteralFromRegion(SuperReg);1063 if (!StrLit)1064 return UnknownVal();1065 SValBuilder &SVB = C.getSValBuilder();1066 NonLoc Idx = ER->getIndex();1067 QualType SizeTy = SVB.getContext().getSizeType();1068 NonLoc LengthVal =1069 SVB.makeIntVal(StrLit->getLength(), SizeTy).castAs<NonLoc>();1070 if (state->assume(SVB.evalBinOpNN(state, BO_LE, Idx, LengthVal,1071 SVB.getConditionType())1072 .castAs<DefinedOrUnknownSVal>(),1073 true))1074 return SVB.evalBinOp(state, BO_Sub, LengthVal, Idx, SizeTy);1075 return UnknownVal();1076 }1077 default:1078 // Other regions (mostly non-data) can't have a reliable C string length.1079 // In this case, an error is emitted and UndefinedVal is returned.1080 // The caller should always be prepared to handle this case.1081 if (NotNullTerm.isEnabled()) {1082 SmallString<120> buf;1083 llvm::raw_svector_ostream os(buf);1084 1085 assert(CurrentFunctionDescription);1086 os << "Argument to " << CurrentFunctionDescription << " is ";1087 1088 if (SummarizeRegion(os, C.getASTContext(), MR))1089 os << ", which is not a null-terminated string";1090 else1091 os << "not a null-terminated string";1092 1093 emitNotCStringBug(C, state, Ex, os.str());1094 }1095 return UndefinedVal();1096 }1097}1098 1099const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,1100 ProgramStateRef &state, const Expr *expr, SVal val) const {1101 // FIXME: use getStringLiteralFromRegion (and remove unused parameters)?1102 1103 // Get the memory region pointed to by the val.1104 const MemRegion *bufRegion = val.getAsRegion();1105 if (!bufRegion)1106 return nullptr;1107 1108 // Strip casts off the memory region.1109 bufRegion = bufRegion->StripCasts();1110 1111 // Cast the memory region to a string region.1112 const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);1113 if (!strRegion)1114 return nullptr;1115 1116 // Return the actual string in the string region.1117 return strRegion->getStringLiteral();1118}1119 1120bool CStringChecker::isFirstBufInBound(CheckerContext &C, ProgramStateRef State,1121 SVal BufVal, QualType BufTy,1122 SVal LengthVal, QualType LengthTy) {1123 // If we do not know that the buffer is long enough we return 'true'.1124 // Otherwise the parent region of this field region would also get1125 // invalidated, which would lead to warnings based on an unknown state.1126 1127 if (LengthVal.isUnknown())1128 return false;1129 1130 // Originally copied from CheckBufferAccess and CheckLocation.1131 SValBuilder &SB = C.getSValBuilder();1132 ASTContext &Ctx = C.getASTContext();1133 1134 QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);1135 1136 std::optional<NonLoc> Length = LengthVal.getAs<NonLoc>();1137 if (!Length)1138 return true; // cf top comment.1139 1140 // Compute the offset of the last element to be accessed: size-1.1141 NonLoc One = SB.makeIntVal(1, LengthTy).castAs<NonLoc>();1142 SVal Offset = SB.evalBinOpNN(State, BO_Sub, *Length, One, LengthTy);1143 if (Offset.isUnknown())1144 return true; // cf top comment1145 NonLoc LastOffset = Offset.castAs<NonLoc>();1146 1147 // Check that the first buffer is sufficiently long.1148 SVal BufStart = SB.evalCast(BufVal, PtrTy, BufTy);1149 std::optional<Loc> BufLoc = BufStart.getAs<Loc>();1150 if (!BufLoc)1151 return true; // cf top comment.1152 1153 SVal BufEnd = SB.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);1154 1155 // Check for out of bound array element access.1156 const MemRegion *R = BufEnd.getAsRegion();1157 if (!R)1158 return true; // cf top comment.1159 1160 const ElementRegion *ER = dyn_cast<ElementRegion>(R);1161 if (!ER)1162 return true; // cf top comment.1163 1164 // Support library functions defined with non-default address spaces1165 assert(ER->getValueType()->getCanonicalTypeUnqualified() ==1166 C.getASTContext().CharTy &&1167 "isFirstBufInBound should only be called with char* ElementRegions");1168 1169 // Get the size of the array.1170 const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());1171 DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, superReg, SB);1172 1173 // Get the index of the accessed element.1174 DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();1175 1176 ProgramStateRef StInBound = State->assumeInBound(Idx, SizeDV, true);1177 1178 return static_cast<bool>(StInBound);1179}1180 1181ProgramStateRef CStringChecker::invalidateDestinationBufferBySize(1182 CheckerContext &C, ProgramStateRef S, const Expr *BufE,1183 ConstCFGElementRef Elem, SVal BufV, SVal SizeV, QualType SizeTy) {1184 auto InvalidationTraitOperations =1185 [&C, S, BufTy = BufE->getType(), BufV, SizeV,1186 SizeTy](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {1187 // If destination buffer is a field region and access is in bound, do1188 // not invalidate its super region.1189 if (MemRegion::FieldRegionKind == R->getKind() &&1190 isFirstBufInBound(C, S, BufV, BufTy, SizeV, SizeTy)) {1191 ITraits.setTrait(1192 R,1193 RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);1194 }1195 return false;1196 };1197 1198 return invalidateBufferAux(C, S, Elem, BufV, InvalidationTraitOperations);1199}1200 1201ProgramStateRef1202CStringChecker::invalidateDestinationBufferAlwaysEscapeSuperRegion(1203 CheckerContext &C, ProgramStateRef S, ConstCFGElementRef Elem, SVal BufV) {1204 auto InvalidationTraitOperations = [](RegionAndSymbolInvalidationTraits &,1205 const MemRegion *R) {1206 return isa<FieldRegion>(R);1207 };1208 1209 return invalidateBufferAux(C, S, Elem, BufV, InvalidationTraitOperations);1210}1211 1212ProgramStateRef CStringChecker::invalidateDestinationBufferNeverOverflows(1213 CheckerContext &C, ProgramStateRef S, ConstCFGElementRef Elem, SVal BufV) {1214 auto InvalidationTraitOperations =1215 [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {1216 if (MemRegion::FieldRegionKind == R->getKind())1217 ITraits.setTrait(1218 R,1219 RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);1220 return false;1221 };1222 1223 return invalidateBufferAux(C, S, Elem, BufV, InvalidationTraitOperations);1224}1225 1226ProgramStateRef CStringChecker::invalidateSourceBuffer(CheckerContext &C,1227 ProgramStateRef S,1228 ConstCFGElementRef Elem,1229 SVal BufV) {1230 auto InvalidationTraitOperations =1231 [](RegionAndSymbolInvalidationTraits &ITraits, const MemRegion *R) {1232 ITraits.setTrait(1233 R->getBaseRegion(),1234 RegionAndSymbolInvalidationTraits::TK_PreserveContents);1235 ITraits.setTrait(R,1236 RegionAndSymbolInvalidationTraits::TK_SuppressEscape);1237 return true;1238 };1239 1240 return invalidateBufferAux(C, S, Elem, BufV, InvalidationTraitOperations);1241}1242 1243ProgramStateRef CStringChecker::invalidateBufferAux(1244 CheckerContext &C, ProgramStateRef State, ConstCFGElementRef Elem, SVal V,1245 llvm::function_ref<bool(RegionAndSymbolInvalidationTraits &,1246 const MemRegion *)>1247 InvalidationTraitOperations) {1248 std::optional<Loc> L = V.getAs<Loc>();1249 if (!L)1250 return State;1251 1252 // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes1253 // some assumptions about the value that CFRefCount can't. Even so, it should1254 // probably be refactored.1255 if (std::optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {1256 const MemRegion *R = MR->getRegion()->StripCasts();1257 1258 // Are we dealing with an ElementRegion? If so, we should be invalidating1259 // the super-region.1260 if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {1261 R = ER->getSuperRegion();1262 // FIXME: What about layers of ElementRegions?1263 }1264 1265 // Invalidate this region.1266 const LocationContext *LCtx = C.getPredecessor()->getLocationContext();1267 RegionAndSymbolInvalidationTraits ITraits;1268 bool CausesPointerEscape = InvalidationTraitOperations(ITraits, R);1269 1270 return State->invalidateRegions(R, Elem, C.blockCount(), LCtx,1271 CausesPointerEscape, nullptr, nullptr,1272 &ITraits);1273 }1274 1275 // If we have a non-region value by chance, just remove the binding.1276 // FIXME: is this necessary or correct? This handles the non-Region1277 // cases. Is it ever valid to store to these?1278 return State->killBinding(*L);1279}1280 1281bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,1282 const MemRegion *MR) {1283 switch (MR->getKind()) {1284 case MemRegion::FunctionCodeRegionKind: {1285 if (const auto *FD = cast<FunctionCodeRegion>(MR)->getDecl())1286 os << "the address of the function '" << *FD << '\'';1287 else1288 os << "the address of a function";1289 return true;1290 }1291 case MemRegion::BlockCodeRegionKind:1292 os << "block text";1293 return true;1294 case MemRegion::BlockDataRegionKind:1295 os << "a block";1296 return true;1297 case MemRegion::CXXThisRegionKind:1298 case MemRegion::CXXTempObjectRegionKind:1299 os << "a C++ temp object of type "1300 << cast<TypedValueRegion>(MR)->getValueType();1301 return true;1302 case MemRegion::NonParamVarRegionKind:1303 os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType();1304 return true;1305 case MemRegion::ParamVarRegionKind:1306 os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType();1307 return true;1308 case MemRegion::FieldRegionKind:1309 os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType();1310 return true;1311 case MemRegion::ObjCIvarRegionKind:1312 os << "an instance variable of type "1313 << cast<TypedValueRegion>(MR)->getValueType();1314 return true;1315 default:1316 return false;1317 }1318}1319 1320bool CStringChecker::memsetAux(const Expr *DstBuffer, ConstCFGElementRef Elem,1321 SVal CharVal, const Expr *Size,1322 CheckerContext &C, ProgramStateRef &State) {1323 SVal MemVal = C.getSVal(DstBuffer);1324 SVal SizeVal = C.getSVal(Size);1325 const MemRegion *MR = MemVal.getAsRegion();1326 if (!MR)1327 return false;1328 1329 // We're about to model memset by producing a "default binding" in the Store.1330 // Our current implementation - RegionStore - doesn't support default bindings1331 // that don't cover the whole base region. So we should first get the offset1332 // and the base region to figure out whether the offset of buffer is 0.1333 RegionOffset Offset = MR->getAsOffset();1334 const MemRegion *BR = Offset.getRegion();1335 1336 std::optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();1337 if (!SizeNL)1338 return false;1339 1340 SValBuilder &svalBuilder = C.getSValBuilder();1341 ASTContext &Ctx = C.getASTContext();1342 1343 // void *memset(void *dest, int ch, size_t count);1344 // For now we can only handle the case of offset is 0 and concrete char value.1345 if (Offset.isValid() && !Offset.hasSymbolicOffset() &&1346 Offset.getOffset() == 0) {1347 // Get the base region's size.1348 DefinedOrUnknownSVal SizeDV = getDynamicExtent(State, BR, svalBuilder);1349 1350 ProgramStateRef StateWholeReg, StateNotWholeReg;1351 std::tie(StateWholeReg, StateNotWholeReg) =1352 State->assume(svalBuilder.evalEQ(State, SizeDV, *SizeNL));1353 1354 // With the semantic of 'memset()', we should convert the CharVal to1355 // unsigned char.1356 CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);1357 1358 ProgramStateRef StateNullChar, StateNonNullChar;1359 std::tie(StateNullChar, StateNonNullChar) =1360 assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);1361 1362 if (StateWholeReg && !StateNotWholeReg && StateNullChar &&1363 !StateNonNullChar) {1364 // If the 'memset()' acts on the whole region of destination buffer and1365 // the value of the second argument of 'memset()' is zero, bind the second1366 // argument's value to the destination buffer with 'default binding'.1367 // FIXME: Since there is no perfect way to bind the non-zero character, we1368 // can only deal with zero value here. In the future, we need to deal with1369 // the binding of non-zero value in the case of whole region.1370 State = State->bindDefaultZero(svalBuilder.makeLoc(BR),1371 C.getLocationContext());1372 } else {1373 // If the destination buffer's extent is not equal to the value of1374 // third argument, just invalidate buffer.1375 State = invalidateDestinationBufferBySize(1376 C, State, DstBuffer, Elem, MemVal, SizeVal, Size->getType());1377 }1378 1379 if (StateNullChar && !StateNonNullChar) {1380 // If the value of the second argument of 'memset()' is zero, set the1381 // string length of destination buffer to 0 directly.1382 State = setCStringLength(State, MR,1383 svalBuilder.makeZeroVal(Ctx.getSizeType()));1384 } else if (!StateNullChar && StateNonNullChar) {1385 SVal NewStrLen = svalBuilder.getMetadataSymbolVal(1386 CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(),1387 C.getLocationContext(), C.blockCount());1388 1389 // If the value of second argument is not zero, then the string length1390 // is at least the size argument.1391 SVal NewStrLenGESize = svalBuilder.evalBinOp(1392 State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType());1393 1394 State = setCStringLength(1395 State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true),1396 MR, NewStrLen);1397 }1398 } else {1399 // If the offset is not zero and char value is not concrete, we can do1400 // nothing but invalidate the buffer.1401 State = invalidateDestinationBufferBySize(C, State, DstBuffer, Elem, MemVal,1402 SizeVal, Size->getType());1403 }1404 return true;1405}1406 1407//===----------------------------------------------------------------------===//1408// evaluation of individual function calls.1409//===----------------------------------------------------------------------===//1410 1411void CStringChecker::evalCopyCommon(CheckerContext &C, const CallEvent &Call,1412 ProgramStateRef state, SizeArgExpr Size,1413 DestinationArgExpr Dest,1414 SourceArgExpr Source, bool Restricted,1415 bool IsMempcpy, CharKind CK) const {1416 CurrentFunctionDescription = "memory copy function";1417 1418 // See if the size argument is zero.1419 const LocationContext *LCtx = C.getLocationContext();1420 SVal sizeVal = state->getSVal(Size.Expression, LCtx);1421 QualType sizeTy = Size.Expression->getType();1422 1423 ProgramStateRef stateZeroSize, stateNonZeroSize;1424 std::tie(stateZeroSize, stateNonZeroSize) =1425 assumeZero(C, state, sizeVal, sizeTy);1426 1427 // Get the value of the Dest.1428 SVal destVal = state->getSVal(Dest.Expression, LCtx);1429 1430 // If the size is zero, there won't be any actual memory access, so1431 // just bind the return value to the destination buffer and return.1432 if (stateZeroSize && !stateNonZeroSize) {1433 stateZeroSize =1434 stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, destVal);1435 C.addTransition(stateZeroSize);1436 return;1437 }1438 1439 // If the size can be nonzero, we have to check the other arguments.1440 if (stateNonZeroSize) {1441 // TODO: If Size is tainted and we cannot prove that it is smaller or equal1442 // to the size of the destination buffer, then emit a warning1443 // that an attacker may provoke a buffer overflow error.1444 state = stateNonZeroSize;1445 1446 // Ensure the destination is not null. If it is NULL there will be a1447 // NULL pointer dereference.1448 state = checkNonNull(C, state, Dest, destVal);1449 if (!state)1450 return;1451 1452 // Get the value of the Src.1453 SVal srcVal = state->getSVal(Source.Expression, LCtx);1454 1455 // Ensure the source is not null. If it is NULL there will be a1456 // NULL pointer dereference.1457 state = checkNonNull(C, state, Source, srcVal);1458 if (!state)1459 return;1460 1461 // Ensure the accesses are valid and that the buffers do not overlap.1462 state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, CK);1463 state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, CK);1464 1465 if (Restricted)1466 state = CheckOverlap(C, state, Size, Dest, Source, CK);1467 1468 if (!state)1469 return;1470 1471 // If this is mempcpy, get the byte after the last byte copied and1472 // bind the expr.1473 if (IsMempcpy) {1474 // Get the byte after the last byte copied.1475 SValBuilder &SvalBuilder = C.getSValBuilder();1476 ASTContext &Ctx = SvalBuilder.getContext();1477 QualType CharPtrTy = getCharPtrType(Ctx, CK);1478 SVal DestRegCharVal =1479 SvalBuilder.evalCast(destVal, CharPtrTy, Dest.Expression->getType());1480 SVal lastElement = C.getSValBuilder().evalBinOp(1481 state, BO_Add, DestRegCharVal, sizeVal, Dest.Expression->getType());1482 // If we don't know how much we copied, we can at least1483 // conjure a return value for later.1484 if (lastElement.isUnknown())1485 lastElement = C.getSValBuilder().conjureSymbolVal(Call, C.blockCount());1486 1487 // The byte after the last byte copied is the return value.1488 state = state->BindExpr(Call.getOriginExpr(), LCtx, lastElement);1489 } else {1490 // All other copies return the destination buffer.1491 // (Well, bcopy() has a void return type, but this won't hurt.)1492 state = state->BindExpr(Call.getOriginExpr(), LCtx, destVal);1493 }1494 1495 // Invalidate the destination (regular invalidation without pointer-escaping1496 // the address of the top-level region).1497 // FIXME: Even if we can't perfectly model the copy, we should see if we1498 // can use LazyCompoundVals to copy the source values into the destination.1499 // This would probably remove any existing bindings past the end of the1500 // copied region, but that's still an improvement over blank invalidation.1501 state = invalidateDestinationBufferBySize(1502 C, state, Dest.Expression, Call.getCFGElementRef(),1503 C.getSVal(Dest.Expression), sizeVal, Size.Expression->getType());1504 1505 // Invalidate the source (const-invalidation without const-pointer-escaping1506 // the address of the top-level region).1507 state = invalidateSourceBuffer(C, state, Call.getCFGElementRef(),1508 C.getSVal(Source.Expression));1509 1510 C.addTransition(state);1511 }1512}1513 1514void CStringChecker::evalMemcpy(CheckerContext &C, const CallEvent &Call,1515 CharKind CK) const {1516 // void *memcpy(void *restrict dst, const void *restrict src, size_t n);1517 // The return value is the address of the destination buffer.1518 DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};1519 SourceArgExpr Src = {{Call.getArgExpr(1), 1}};1520 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};1521 1522 ProgramStateRef State = C.getState();1523 1524 constexpr bool IsRestricted = true;1525 constexpr bool IsMempcpy = false;1526 evalCopyCommon(C, Call, State, Size, Dest, Src, IsRestricted, IsMempcpy, CK);1527}1528 1529void CStringChecker::evalMempcpy(CheckerContext &C, const CallEvent &Call,1530 CharKind CK) const {1531 // void *mempcpy(void *restrict dst, const void *restrict src, size_t n);1532 // The return value is a pointer to the byte following the last written byte.1533 DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};1534 SourceArgExpr Src = {{Call.getArgExpr(1), 1}};1535 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};1536 1537 constexpr bool IsRestricted = true;1538 constexpr bool IsMempcpy = true;1539 evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,1540 IsMempcpy, CK);1541}1542 1543void CStringChecker::evalMemmove(CheckerContext &C, const CallEvent &Call,1544 CharKind CK) const {1545 // void *memmove(void *dst, const void *src, size_t n);1546 // The return value is the address of the destination buffer.1547 DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};1548 SourceArgExpr Src = {{Call.getArgExpr(1), 1}};1549 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};1550 1551 constexpr bool IsRestricted = false;1552 constexpr bool IsMempcpy = false;1553 evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,1554 IsMempcpy, CK);1555}1556 1557void CStringChecker::evalBcopy(CheckerContext &C, const CallEvent &Call) const {1558 // void bcopy(const void *src, void *dst, size_t n);1559 SourceArgExpr Src{{Call.getArgExpr(0), 0}};1560 DestinationArgExpr Dest = {{Call.getArgExpr(1), 1}};1561 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};1562 1563 constexpr bool IsRestricted = false;1564 constexpr bool IsMempcpy = false;1565 evalCopyCommon(C, Call, C.getState(), Size, Dest, Src, IsRestricted,1566 IsMempcpy, CharKind::Regular);1567}1568 1569void CStringChecker::evalMemcmp(CheckerContext &C, const CallEvent &Call,1570 CharKind CK) const {1571 // int memcmp(const void *s1, const void *s2, size_t n);1572 CurrentFunctionDescription = "memory comparison function";1573 1574 AnyArgExpr Left = {Call.getArgExpr(0), 0};1575 AnyArgExpr Right = {Call.getArgExpr(1), 1};1576 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};1577 1578 ProgramStateRef State = C.getState();1579 SValBuilder &Builder = C.getSValBuilder();1580 const LocationContext *LCtx = C.getLocationContext();1581 1582 // See if the size argument is zero.1583 SVal sizeVal = State->getSVal(Size.Expression, LCtx);1584 QualType sizeTy = Size.Expression->getType();1585 1586 ProgramStateRef stateZeroSize, stateNonZeroSize;1587 std::tie(stateZeroSize, stateNonZeroSize) =1588 assumeZero(C, State, sizeVal, sizeTy);1589 1590 // If the size can be zero, the result will be 0 in that case, and we don't1591 // have to check either of the buffers.1592 if (stateZeroSize) {1593 State = stateZeroSize;1594 State = State->BindExpr(Call.getOriginExpr(), LCtx,1595 Builder.makeZeroVal(Call.getResultType()));1596 C.addTransition(State);1597 }1598 1599 // If the size can be nonzero, we have to check the other arguments.1600 if (stateNonZeroSize) {1601 State = stateNonZeroSize;1602 // If we know the two buffers are the same, we know the result is 0.1603 // First, get the two buffers' addresses. Another checker will have already1604 // made sure they're not undefined.1605 DefinedOrUnknownSVal LV =1606 State->getSVal(Left.Expression, LCtx).castAs<DefinedOrUnknownSVal>();1607 DefinedOrUnknownSVal RV =1608 State->getSVal(Right.Expression, LCtx).castAs<DefinedOrUnknownSVal>();1609 1610 // See if they are the same.1611 ProgramStateRef SameBuffer, NotSameBuffer;1612 std::tie(SameBuffer, NotSameBuffer) =1613 State->assume(Builder.evalEQ(State, LV, RV));1614 1615 // If the two arguments are the same buffer, we know the result is 0,1616 // and we only need to check one size.1617 if (SameBuffer && !NotSameBuffer) {1618 State = SameBuffer;1619 State = CheckBufferAccess(C, State, Left, Size, AccessKind::read);1620 if (State) {1621 State = SameBuffer->BindExpr(Call.getOriginExpr(), LCtx,1622 Builder.makeZeroVal(Call.getResultType()));1623 C.addTransition(State);1624 }1625 return;1626 }1627 1628 // If the two arguments might be different buffers, we have to check1629 // the size of both of them.1630 assert(NotSameBuffer);1631 State = CheckBufferAccess(C, State, Right, Size, AccessKind::read, CK);1632 State = CheckBufferAccess(C, State, Left, Size, AccessKind::read, CK);1633 if (State) {1634 // The return value is the comparison result, which we don't know.1635 SVal CmpV = Builder.conjureSymbolVal(Call, C.blockCount());1636 State = State->BindExpr(Call.getOriginExpr(), LCtx, CmpV);1637 C.addTransition(State);1638 }1639 }1640}1641 1642void CStringChecker::evalstrLength(CheckerContext &C,1643 const CallEvent &Call) const {1644 // size_t strlen(const char *s);1645 evalstrLengthCommon(C, Call, /* IsStrnlen = */ false);1646}1647 1648void CStringChecker::evalstrnLength(CheckerContext &C,1649 const CallEvent &Call) const {1650 // size_t strnlen(const char *s, size_t maxlen);1651 evalstrLengthCommon(C, Call, /* IsStrnlen = */ true);1652}1653 1654void CStringChecker::evalstrLengthCommon(CheckerContext &C,1655 const CallEvent &Call,1656 bool IsStrnlen) const {1657 CurrentFunctionDescription = "string length function";1658 ProgramStateRef state = C.getState();1659 const LocationContext *LCtx = C.getLocationContext();1660 1661 if (IsStrnlen) {1662 const Expr *maxlenExpr = Call.getArgExpr(1);1663 SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);1664 1665 ProgramStateRef stateZeroSize, stateNonZeroSize;1666 std::tie(stateZeroSize, stateNonZeroSize) =1667 assumeZero(C, state, maxlenVal, maxlenExpr->getType());1668 1669 // If the size can be zero, the result will be 0 in that case, and we don't1670 // have to check the string itself.1671 if (stateZeroSize) {1672 SVal zero = C.getSValBuilder().makeZeroVal(Call.getResultType());1673 stateZeroSize = stateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, zero);1674 C.addTransition(stateZeroSize);1675 }1676 1677 // If the size is GUARANTEED to be zero, we're done!1678 if (!stateNonZeroSize)1679 return;1680 1681 // Otherwise, record the assumption that the size is nonzero.1682 state = stateNonZeroSize;1683 }1684 1685 // Check that the string argument is non-null.1686 AnyArgExpr Arg = {Call.getArgExpr(0), 0};1687 SVal ArgVal = state->getSVal(Arg.Expression, LCtx);1688 state = checkNonNull(C, state, Arg, ArgVal);1689 1690 if (!state)1691 return;1692 1693 SVal strLength = getCStringLength(C, state, Arg.Expression, ArgVal);1694 1695 // If the argument isn't a valid C string, there's no valid state to1696 // transition to.1697 if (strLength.isUndef())1698 return;1699 1700 DefinedOrUnknownSVal result = UnknownVal();1701 1702 // If the check is for strnlen() then bind the return value to no more than1703 // the maxlen value.1704 if (IsStrnlen) {1705 QualType cmpTy = C.getSValBuilder().getConditionType();1706 1707 // It's a little unfortunate to be getting this again,1708 // but it's not that expensive...1709 const Expr *maxlenExpr = Call.getArgExpr(1);1710 SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);1711 1712 std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();1713 std::optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();1714 1715 if (strLengthNL && maxlenValNL) {1716 ProgramStateRef stateStringTooLong, stateStringNotTooLong;1717 1718 // Check if the strLength is greater than the maxlen.1719 std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(1720 C.getSValBuilder()1721 .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)1722 .castAs<DefinedOrUnknownSVal>());1723 1724 if (stateStringTooLong && !stateStringNotTooLong) {1725 // If the string is longer than maxlen, return maxlen.1726 result = *maxlenValNL;1727 } else if (stateStringNotTooLong && !stateStringTooLong) {1728 // If the string is shorter than maxlen, return its length.1729 result = *strLengthNL;1730 }1731 }1732 1733 if (result.isUnknown()) {1734 // If we don't have enough information for a comparison, there's1735 // no guarantee the full string length will actually be returned.1736 // All we know is the return value is the min of the string length1737 // and the limit. This is better than nothing.1738 result = C.getSValBuilder().conjureSymbolVal(Call, C.blockCount());1739 NonLoc resultNL = result.castAs<NonLoc>();1740 1741 if (strLengthNL) {1742 state = state->assume(C.getSValBuilder().evalBinOpNN(1743 state, BO_LE, resultNL, *strLengthNL, cmpTy)1744 .castAs<DefinedOrUnknownSVal>(), true);1745 }1746 1747 if (maxlenValNL) {1748 state = state->assume(C.getSValBuilder().evalBinOpNN(1749 state, BO_LE, resultNL, *maxlenValNL, cmpTy)1750 .castAs<DefinedOrUnknownSVal>(), true);1751 }1752 }1753 1754 } else {1755 // This is a plain strlen(), not strnlen().1756 result = strLength.castAs<DefinedOrUnknownSVal>();1757 1758 // If we don't know the length of the string, conjure a return1759 // value, so it can be used in constraints, at least.1760 if (result.isUnknown()) {1761 result = C.getSValBuilder().conjureSymbolVal(Call, C.blockCount());1762 }1763 }1764 1765 // Bind the return value.1766 assert(!result.isUnknown() && "Should have conjured a value by now");1767 state = state->BindExpr(Call.getOriginExpr(), LCtx, result);1768 C.addTransition(state);1769}1770 1771void CStringChecker::evalStrcpy(CheckerContext &C,1772 const CallEvent &Call) const {1773 // char *strcpy(char *restrict dst, const char *restrict src);1774 evalStrcpyCommon(C, Call,1775 /* ReturnEnd = */ false,1776 /* IsBounded = */ false,1777 /* appendK = */ ConcatFnKind::none);1778}1779 1780void CStringChecker::evalStrncpy(CheckerContext &C,1781 const CallEvent &Call) const {1782 // char *strncpy(char *restrict dst, const char *restrict src, size_t n);1783 evalStrcpyCommon(C, Call,1784 /* ReturnEnd = */ false,1785 /* IsBounded = */ true,1786 /* appendK = */ ConcatFnKind::none);1787}1788 1789void CStringChecker::evalStpcpy(CheckerContext &C,1790 const CallEvent &Call) const {1791 // char *stpcpy(char *restrict dst, const char *restrict src);1792 evalStrcpyCommon(C, Call,1793 /* ReturnEnd = */ true,1794 /* IsBounded = */ false,1795 /* appendK = */ ConcatFnKind::none);1796}1797 1798void CStringChecker::evalStrlcpy(CheckerContext &C,1799 const CallEvent &Call) const {1800 // size_t strlcpy(char *dest, const char *src, size_t size);1801 evalStrcpyCommon(C, Call,1802 /* ReturnEnd = */ true,1803 /* IsBounded = */ true,1804 /* appendK = */ ConcatFnKind::none,1805 /* returnPtr = */ false);1806}1807 1808void CStringChecker::evalStrcat(CheckerContext &C,1809 const CallEvent &Call) const {1810 // char *strcat(char *restrict s1, const char *restrict s2);1811 evalStrcpyCommon(C, Call,1812 /* ReturnEnd = */ false,1813 /* IsBounded = */ false,1814 /* appendK = */ ConcatFnKind::strcat);1815}1816 1817void CStringChecker::evalStrncat(CheckerContext &C,1818 const CallEvent &Call) const {1819 // char *strncat(char *restrict s1, const char *restrict s2, size_t n);1820 evalStrcpyCommon(C, Call,1821 /* ReturnEnd = */ false,1822 /* IsBounded = */ true,1823 /* appendK = */ ConcatFnKind::strcat);1824}1825 1826void CStringChecker::evalStrlcat(CheckerContext &C,1827 const CallEvent &Call) const {1828 // size_t strlcat(char *dst, const char *src, size_t size);1829 // It will append at most size - strlen(dst) - 1 bytes,1830 // NULL-terminating the result.1831 evalStrcpyCommon(C, Call,1832 /* ReturnEnd = */ false,1833 /* IsBounded = */ true,1834 /* appendK = */ ConcatFnKind::strlcat,1835 /* returnPtr = */ false);1836}1837 1838void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallEvent &Call,1839 bool ReturnEnd, bool IsBounded,1840 ConcatFnKind appendK,1841 bool returnPtr) const {1842 if (appendK == ConcatFnKind::none)1843 CurrentFunctionDescription = "string copy function";1844 else1845 CurrentFunctionDescription = "string concatenation function";1846 1847 ProgramStateRef state = C.getState();1848 const LocationContext *LCtx = C.getLocationContext();1849 1850 // Check that the destination is non-null.1851 DestinationArgExpr Dst = {{Call.getArgExpr(0), 0}};1852 SVal DstVal = state->getSVal(Dst.Expression, LCtx);1853 state = checkNonNull(C, state, Dst, DstVal);1854 if (!state)1855 return;1856 1857 // Check that the source is non-null.1858 SourceArgExpr srcExpr = {{Call.getArgExpr(1), 1}};1859 SVal srcVal = state->getSVal(srcExpr.Expression, LCtx);1860 state = checkNonNull(C, state, srcExpr, srcVal);1861 if (!state)1862 return;1863 1864 // Get the string length of the source.1865 SVal strLength = getCStringLength(C, state, srcExpr.Expression, srcVal);1866 std::optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();1867 1868 // Get the string length of the destination buffer.1869 SVal dstStrLength = getCStringLength(C, state, Dst.Expression, DstVal);1870 std::optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();1871 1872 // If the source isn't a valid C string, give up.1873 if (strLength.isUndef())1874 return;1875 1876 SValBuilder &svalBuilder = C.getSValBuilder();1877 QualType cmpTy = svalBuilder.getConditionType();1878 QualType sizeTy = svalBuilder.getContext().getSizeType();1879 1880 // These two values allow checking two kinds of errors:1881 // - actual overflows caused by a source that doesn't fit in the destination1882 // - potential overflows caused by a bound that could exceed the destination1883 SVal amountCopied = UnknownVal();1884 SVal maxLastElementIndex = UnknownVal();1885 const char *boundWarning = nullptr;1886 1887 // FIXME: Why do we choose the srcExpr if the access has no size?1888 // Note that the 3rd argument of the call would be the size parameter.1889 SizeArgExpr SrcExprAsSizeDummy = {1890 {srcExpr.Expression, srcExpr.ArgumentIndex}};1891 state = CheckOverlap(1892 C, state,1893 (IsBounded ? SizeArgExpr{{Call.getArgExpr(2), 2}} : SrcExprAsSizeDummy),1894 Dst, srcExpr);1895 1896 if (!state)1897 return;1898 1899 // If the function is strncpy, strncat, etc... it is bounded.1900 if (IsBounded) {1901 // Get the max number of characters to copy.1902 SizeArgExpr lenExpr = {{Call.getArgExpr(2), 2}};1903 SVal lenVal = state->getSVal(lenExpr.Expression, LCtx);1904 1905 // Protect against misdeclared strncpy().1906 lenVal =1907 svalBuilder.evalCast(lenVal, sizeTy, lenExpr.Expression->getType());1908 1909 std::optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();1910 1911 // If we know both values, we might be able to figure out how much1912 // we're copying.1913 if (strLengthNL && lenValNL) {1914 switch (appendK) {1915 case ConcatFnKind::none:1916 case ConcatFnKind::strcat: {1917 ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;1918 // Check if the max number to copy is less than the length of the src.1919 // If the bound is equal to the source length, strncpy won't null-1920 // terminate the result!1921 std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(1922 svalBuilder1923 .evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)1924 .castAs<DefinedOrUnknownSVal>());1925 1926 if (stateSourceTooLong && !stateSourceNotTooLong) {1927 // Max number to copy is less than the length of the src, so the1928 // actual strLength copied is the max number arg.1929 state = stateSourceTooLong;1930 amountCopied = lenVal;1931 1932 } else if (!stateSourceTooLong && stateSourceNotTooLong) {1933 // The source buffer entirely fits in the bound.1934 state = stateSourceNotTooLong;1935 amountCopied = strLength;1936 }1937 break;1938 }1939 case ConcatFnKind::strlcat:1940 if (!dstStrLengthNL)1941 return;1942 1943 // amountCopied = min (size - dstLen - 1 , srcLen)1944 SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,1945 *dstStrLengthNL, sizeTy);1946 if (!isa<NonLoc>(freeSpace))1947 return;1948 freeSpace =1949 svalBuilder.evalBinOp(state, BO_Sub, freeSpace,1950 svalBuilder.makeIntVal(1, sizeTy), sizeTy);1951 std::optional<NonLoc> freeSpaceNL = freeSpace.getAs<NonLoc>();1952 1953 // While unlikely, it is possible that the subtraction is1954 // too complex to compute, let's check whether it succeeded.1955 if (!freeSpaceNL)1956 return;1957 SVal hasEnoughSpace = svalBuilder.evalBinOpNN(1958 state, BO_LE, *strLengthNL, *freeSpaceNL, cmpTy);1959 1960 ProgramStateRef TrueState, FalseState;1961 std::tie(TrueState, FalseState) =1962 state->assume(hasEnoughSpace.castAs<DefinedOrUnknownSVal>());1963 1964 // srcStrLength <= size - dstStrLength -11965 if (TrueState && !FalseState) {1966 amountCopied = strLength;1967 }1968 1969 // srcStrLength > size - dstStrLength -11970 if (!TrueState && FalseState) {1971 amountCopied = freeSpace;1972 }1973 1974 if (TrueState && FalseState)1975 amountCopied = UnknownVal();1976 break;1977 }1978 }1979 // We still want to know if the bound is known to be too large.1980 if (lenValNL) {1981 switch (appendK) {1982 case ConcatFnKind::strcat:1983 // For strncat, the check is strlen(dst) + lenVal < sizeof(dst)1984 1985 // Get the string length of the destination. If the destination is1986 // memory that can't have a string length, we shouldn't be copying1987 // into it anyway.1988 if (dstStrLength.isUndef())1989 return;1990 1991 if (dstStrLengthNL) {1992 maxLastElementIndex = svalBuilder.evalBinOpNN(1993 state, BO_Add, *lenValNL, *dstStrLengthNL, sizeTy);1994 1995 boundWarning = "Size argument is greater than the free space in the "1996 "destination buffer";1997 }1998 break;1999 case ConcatFnKind::none:2000 case ConcatFnKind::strlcat:2001 // For strncpy and strlcat, this is just checking2002 // that lenVal <= sizeof(dst).2003 // (Yes, strncpy and strncat differ in how they treat termination.2004 // strncat ALWAYS terminates, but strncpy doesn't.)2005 2006 // We need a special case for when the copy size is zero, in which2007 // case strncpy will do no work at all. Our bounds check uses n-12008 // as the last element accessed, so n == 0 is problematic.2009 ProgramStateRef StateZeroSize, StateNonZeroSize;2010 std::tie(StateZeroSize, StateNonZeroSize) =2011 assumeZero(C, state, *lenValNL, sizeTy);2012 2013 // If the size is known to be zero, we're done.2014 if (StateZeroSize && !StateNonZeroSize) {2015 if (returnPtr) {2016 StateZeroSize =2017 StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, DstVal);2018 } else {2019 if (appendK == ConcatFnKind::none) {2020 // strlcpy returns strlen(src)2021 StateZeroSize = StateZeroSize->BindExpr(Call.getOriginExpr(),2022 LCtx, strLength);2023 } else {2024 // strlcat returns strlen(src) + strlen(dst)2025 SVal retSize = svalBuilder.evalBinOp(2026 state, BO_Add, strLength, dstStrLength, sizeTy);2027 StateZeroSize =2028 StateZeroSize->BindExpr(Call.getOriginExpr(), LCtx, retSize);2029 }2030 }2031 C.addTransition(StateZeroSize);2032 return;2033 }2034 2035 // Otherwise, go ahead and figure out the last element we'll touch.2036 // We don't record the non-zero assumption here because we can't2037 // be sure. We won't warn on a possible zero.2038 NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();2039 maxLastElementIndex =2040 svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, one, sizeTy);2041 boundWarning = "Size argument is greater than the length of the "2042 "destination buffer";2043 break;2044 }2045 }2046 } else {2047 // The function isn't bounded. The amount copied should match the length2048 // of the source buffer.2049 amountCopied = strLength;2050 }2051 2052 assert(state);2053 2054 // This represents the number of characters copied into the destination2055 // buffer. (It may not actually be the strlen if the destination buffer2056 // is not terminated.)2057 SVal finalStrLength = UnknownVal();2058 SVal strlRetVal = UnknownVal();2059 2060 if (appendK == ConcatFnKind::none && !returnPtr) {2061 // strlcpy returns the sizeof(src)2062 strlRetVal = strLength;2063 }2064 2065 // If this is an appending function (strcat, strncat...) then set the2066 // string length to strlen(src) + strlen(dst) since the buffer will2067 // ultimately contain both.2068 if (appendK != ConcatFnKind::none) {2069 // Get the string length of the destination. If the destination is memory2070 // that can't have a string length, we shouldn't be copying into it anyway.2071 if (dstStrLength.isUndef())2072 return;2073 2074 if (appendK == ConcatFnKind::strlcat && dstStrLengthNL && strLengthNL) {2075 strlRetVal = svalBuilder.evalBinOpNN(state, BO_Add, *strLengthNL,2076 *dstStrLengthNL, sizeTy);2077 }2078 2079 std::optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>();2080 2081 // If we know both string lengths, we might know the final string length.2082 if (amountCopiedNL && dstStrLengthNL) {2083 // Make sure the two lengths together don't overflow a size_t.2084 state = checkAdditionOverflow(C, state, *amountCopiedNL, *dstStrLengthNL);2085 if (!state)2086 return;2087 2088 finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *amountCopiedNL,2089 *dstStrLengthNL, sizeTy);2090 }2091 2092 // If we couldn't get a single value for the final string length,2093 // we can at least bound it by the individual lengths.2094 if (finalStrLength.isUnknown()) {2095 // Try to get a "hypothetical" string length symbol, which we can later2096 // set as a real value if that turns out to be the case.2097 finalStrLength =2098 getCStringLength(C, state, Call.getOriginExpr(), DstVal, true);2099 assert(!finalStrLength.isUndef());2100 2101 if (std::optional<NonLoc> finalStrLengthNL =2102 finalStrLength.getAs<NonLoc>()) {2103 if (amountCopiedNL && appendK == ConcatFnKind::none) {2104 // we overwrite dst string with the src2105 // finalStrLength >= srcStrLength2106 SVal sourceInResult = svalBuilder.evalBinOpNN(2107 state, BO_GE, *finalStrLengthNL, *amountCopiedNL, cmpTy);2108 state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(),2109 true);2110 if (!state)2111 return;2112 }2113 2114 if (dstStrLengthNL && appendK != ConcatFnKind::none) {2115 // we extend the dst string with the src2116 // finalStrLength >= dstStrLength2117 SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE,2118 *finalStrLengthNL,2119 *dstStrLengthNL,2120 cmpTy);2121 state =2122 state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true);2123 if (!state)2124 return;2125 }2126 }2127 }2128 2129 } else {2130 // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and2131 // the final string length will match the input string length.2132 finalStrLength = amountCopied;2133 }2134 2135 SVal Result;2136 2137 if (returnPtr) {2138 // The final result of the function will either be a pointer past the last2139 // copied element, or a pointer to the start of the destination buffer.2140 Result = (ReturnEnd ? UnknownVal() : DstVal);2141 } else {2142 if (appendK == ConcatFnKind::strlcat || appendK == ConcatFnKind::none)2143 //strlcpy, strlcat2144 Result = strlRetVal;2145 else2146 Result = finalStrLength;2147 }2148 2149 assert(state);2150 2151 // If the destination is a MemRegion, try to check for a buffer overflow and2152 // record the new string length.2153 if (std::optional<loc::MemRegionVal> dstRegVal =2154 DstVal.getAs<loc::MemRegionVal>()) {2155 QualType ptrTy = Dst.Expression->getType();2156 2157 // If we have an exact value on a bounded copy, use that to check for2158 // overflows, rather than our estimate about how much is actually copied.2159 if (std::optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {2160 SVal maxLastElement =2161 svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, *maxLastNL, ptrTy);2162 2163 // Check if the first byte of the destination is writable.2164 state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);2165 if (!state)2166 return;2167 // Check if the last byte of the destination is writable.2168 state = CheckLocation(C, state, Dst, maxLastElement, AccessKind::write);2169 if (!state)2170 return;2171 }2172 2173 // Then, if the final length is known...2174 if (std::optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {2175 SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,2176 *knownStrLength, ptrTy);2177 2178 // ...and we haven't checked the bound, we'll check the actual copy.2179 if (!boundWarning) {2180 // Check if the first byte of the destination is writable.2181 state = CheckLocation(C, state, Dst, DstVal, AccessKind::write);2182 if (!state)2183 return;2184 // Check if the last byte of the destination is writable.2185 state = CheckLocation(C, state, Dst, lastElement, AccessKind::write);2186 if (!state)2187 return;2188 }2189 2190 // If this is a stpcpy-style copy, the last element is the return value.2191 if (returnPtr && ReturnEnd)2192 Result = lastElement;2193 }2194 2195 // For bounded method, amountCopied take the minimum of two values,2196 // for ConcatFnKind::strlcat:2197 // amountCopied = min (size - dstLen - 1 , srcLen)2198 // for others:2199 // amountCopied = min (srcLen, size)2200 // So even if we don't know about amountCopied, as long as one of them will2201 // not cause an out-of-bound access, the whole function's operation will not2202 // too, that will avoid invalidating the superRegion of data member in that2203 // situation.2204 bool CouldAccessOutOfBound = true;2205 if (IsBounded && amountCopied.isUnknown()) {2206 auto CouldAccessOutOfBoundForSVal =2207 [&](std::optional<NonLoc> Val) -> bool {2208 if (!Val)2209 return true;2210 return !isFirstBufInBound(C, state, C.getSVal(Dst.Expression),2211 Dst.Expression->getType(), *Val,2212 C.getASTContext().getSizeType());2213 };2214 2215 CouldAccessOutOfBound = CouldAccessOutOfBoundForSVal(strLengthNL);2216 2217 if (CouldAccessOutOfBound) {2218 // Get the max number of characters to copy.2219 const Expr *LenExpr = Call.getArgExpr(2);2220 SVal LenVal = state->getSVal(LenExpr, LCtx);2221 2222 // Protect against misdeclared strncpy().2223 LenVal = svalBuilder.evalCast(LenVal, sizeTy, LenExpr->getType());2224 2225 // Because analyzer doesn't handle expressions like `size -2226 // dstLen - 1` very well, we roughly use `size` for2227 // ConcatFnKind::strlcat here, same with other concat kinds.2228 CouldAccessOutOfBound =2229 CouldAccessOutOfBoundForSVal(LenVal.getAs<NonLoc>());2230 }2231 }2232 2233 // Invalidate the destination (regular invalidation without pointer-escaping2234 // the address of the top-level region). This must happen before we set the2235 // C string length because invalidation will clear the length.2236 // FIXME: Even if we can't perfectly model the copy, we should see if we2237 // can use LazyCompoundVals to copy the source values into the destination.2238 // This would probably remove any existing bindings past the end of the2239 // string, but that's still an improvement over blank invalidation.2240 if (CouldAccessOutOfBound)2241 state = invalidateDestinationBufferBySize(2242 C, state, Dst.Expression, Call.getCFGElementRef(), *dstRegVal,2243 amountCopied, C.getASTContext().getSizeType());2244 else2245 state = invalidateDestinationBufferNeverOverflows(2246 C, state, Call.getCFGElementRef(), *dstRegVal);2247 2248 // Invalidate the source (const-invalidation without const-pointer-escaping2249 // the address of the top-level region).2250 state = invalidateSourceBuffer(C, state, Call.getCFGElementRef(), srcVal);2251 2252 // Set the C string length of the destination, if we know it.2253 if (IsBounded && (appendK == ConcatFnKind::none)) {2254 // strncpy is annoying in that it doesn't guarantee to null-terminate2255 // the result string. If the original string didn't fit entirely inside2256 // the bound (including the null-terminator), we don't know how long the2257 // result is.2258 if (amountCopied != strLength)2259 finalStrLength = UnknownVal();2260 }2261 state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength);2262 }2263 2264 assert(state);2265 2266 if (returnPtr) {2267 // If this is a stpcpy-style copy, but we were unable to check for a buffer2268 // overflow, we still need a result. Conjure a return value.2269 if (ReturnEnd && Result.isUnknown()) {2270 Result = svalBuilder.conjureSymbolVal(Call, C.blockCount());2271 }2272 }2273 // Set the return value.2274 state = state->BindExpr(Call.getOriginExpr(), LCtx, Result);2275 C.addTransition(state);2276}2277 2278void CStringChecker::evalStrxfrm(CheckerContext &C,2279 const CallEvent &Call) const {2280 // size_t strxfrm(char *dest, const char *src, size_t n);2281 CurrentFunctionDescription = "locale transformation function";2282 2283 ProgramStateRef State = C.getState();2284 const LocationContext *LCtx = C.getLocationContext();2285 SValBuilder &SVB = C.getSValBuilder();2286 2287 // Get arguments2288 DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};2289 SourceArgExpr Source = {{Call.getArgExpr(1), 1}};2290 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};2291 2292 // `src` can never be null2293 SVal SrcVal = State->getSVal(Source.Expression, LCtx);2294 State = checkNonNull(C, State, Source, SrcVal);2295 if (!State)2296 return;2297 2298 // Buffer must not overlap2299 State = CheckOverlap(C, State, Size, Dest, Source, CK_Regular);2300 if (!State)2301 return;2302 2303 // The function returns an implementation-defined length needed for2304 // transformation2305 SVal RetVal = SVB.conjureSymbolVal(Call, C.blockCount());2306 2307 auto BindReturnAndTransition = [&RetVal, &Call, LCtx,2308 &C](ProgramStateRef State) {2309 if (State) {2310 State = State->BindExpr(Call.getOriginExpr(), LCtx, RetVal);2311 C.addTransition(State);2312 }2313 };2314 2315 // Check if size is zero2316 SVal SizeVal = State->getSVal(Size.Expression, LCtx);2317 QualType SizeTy = Size.Expression->getType();2318 2319 auto [StateZeroSize, StateSizeNonZero] =2320 assumeZero(C, State, SizeVal, SizeTy);2321 2322 // We can't assume anything about size, just bind the return value and be done2323 if (!StateZeroSize && !StateSizeNonZero)2324 return BindReturnAndTransition(State);2325 2326 // If `n` is 0, we just return the implementation defined length2327 if (StateZeroSize && !StateSizeNonZero)2328 return BindReturnAndTransition(StateZeroSize);2329 2330 // If `n` is not 0, `dest` can not be null.2331 SVal DestVal = StateSizeNonZero->getSVal(Dest.Expression, LCtx);2332 StateSizeNonZero = checkNonNull(C, StateSizeNonZero, Dest, DestVal);2333 if (!StateSizeNonZero)2334 return;2335 2336 // Check that we can write to the destination buffer2337 StateSizeNonZero = CheckBufferAccess(C, StateSizeNonZero, Dest, Size,2338 AccessKind::write, CK_Regular);2339 if (!StateSizeNonZero)2340 return;2341 2342 // Success: return value < `n`2343 // Failure: return value >= `n`2344 auto ComparisonVal = SVB.evalBinOp(StateSizeNonZero, BO_LT, RetVal, SizeVal,2345 SVB.getConditionType())2346 .getAs<DefinedOrUnknownSVal>();2347 if (!ComparisonVal) {2348 // Fallback: invalidate the buffer.2349 StateSizeNonZero = invalidateDestinationBufferBySize(2350 C, StateSizeNonZero, Dest.Expression, Call.getCFGElementRef(), DestVal,2351 SizeVal, Size.Expression->getType());2352 return BindReturnAndTransition(StateSizeNonZero);2353 }2354 2355 auto [StateSuccess, StateFailure] = StateSizeNonZero->assume(*ComparisonVal);2356 2357 if (StateSuccess) {2358 // The transformation invalidated the buffer.2359 StateSuccess = invalidateDestinationBufferBySize(2360 C, StateSuccess, Dest.Expression, Call.getCFGElementRef(), DestVal,2361 SizeVal, Size.Expression->getType());2362 BindReturnAndTransition(StateSuccess);2363 // Fallthrough: We also want to add a transition to the failure state below.2364 }2365 2366 if (StateFailure) {2367 // `dest` buffer content is undefined2368 if (auto DestLoc = DestVal.getAs<loc::MemRegionVal>()) {2369 StateFailure = StateFailure->killBinding(*DestLoc);2370 StateFailure =2371 StateFailure->bindDefaultInitial(*DestLoc, UndefinedVal{}, LCtx);2372 }2373 2374 BindReturnAndTransition(StateFailure);2375 }2376}2377 2378void CStringChecker::evalStrcmp(CheckerContext &C,2379 const CallEvent &Call) const {2380 //int strcmp(const char *s1, const char *s2);2381 evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ false);2382}2383 2384void CStringChecker::evalStrncmp(CheckerContext &C,2385 const CallEvent &Call) const {2386 //int strncmp(const char *s1, const char *s2, size_t n);2387 evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ false);2388}2389 2390void CStringChecker::evalStrcasecmp(CheckerContext &C,2391 const CallEvent &Call) const {2392 //int strcasecmp(const char *s1, const char *s2);2393 evalStrcmpCommon(C, Call, /* IsBounded = */ false, /* IgnoreCase = */ true);2394}2395 2396void CStringChecker::evalStrncasecmp(CheckerContext &C,2397 const CallEvent &Call) const {2398 //int strncasecmp(const char *s1, const char *s2, size_t n);2399 evalStrcmpCommon(C, Call, /* IsBounded = */ true, /* IgnoreCase = */ true);2400}2401 2402void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallEvent &Call,2403 bool IsBounded, bool IgnoreCase) const {2404 CurrentFunctionDescription = "string comparison function";2405 ProgramStateRef state = C.getState();2406 const LocationContext *LCtx = C.getLocationContext();2407 2408 // Check that the first string is non-null2409 AnyArgExpr Left = {Call.getArgExpr(0), 0};2410 SVal LeftVal = state->getSVal(Left.Expression, LCtx);2411 state = checkNonNull(C, state, Left, LeftVal);2412 if (!state)2413 return;2414 2415 // Check that the second string is non-null.2416 AnyArgExpr Right = {Call.getArgExpr(1), 1};2417 SVal RightVal = state->getSVal(Right.Expression, LCtx);2418 state = checkNonNull(C, state, Right, RightVal);2419 if (!state)2420 return;2421 2422 // Get the string length of the first string or give up.2423 SVal LeftLength = getCStringLength(C, state, Left.Expression, LeftVal);2424 if (LeftLength.isUndef())2425 return;2426 2427 // Get the string length of the second string or give up.2428 SVal RightLength = getCStringLength(C, state, Right.Expression, RightVal);2429 if (RightLength.isUndef())2430 return;2431 2432 // If we know the two buffers are the same, we know the result is 0.2433 // First, get the two buffers' addresses. Another checker will have already2434 // made sure they're not undefined.2435 DefinedOrUnknownSVal LV = LeftVal.castAs<DefinedOrUnknownSVal>();2436 DefinedOrUnknownSVal RV = RightVal.castAs<DefinedOrUnknownSVal>();2437 2438 // See if they are the same.2439 SValBuilder &svalBuilder = C.getSValBuilder();2440 DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);2441 ProgramStateRef StSameBuf, StNotSameBuf;2442 std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);2443 2444 // If the two arguments might be the same buffer, we know the result is 0,2445 // and we only need to check one size.2446 if (StSameBuf) {2447 StSameBuf =2448 StSameBuf->BindExpr(Call.getOriginExpr(), LCtx,2449 svalBuilder.makeZeroVal(Call.getResultType()));2450 C.addTransition(StSameBuf);2451 2452 // If the two arguments are GUARANTEED to be the same, we're done!2453 if (!StNotSameBuf)2454 return;2455 }2456 2457 assert(StNotSameBuf);2458 state = StNotSameBuf;2459 2460 // At this point we can go about comparing the two buffers.2461 // For now, we only do this if they're both known string literals.2462 2463 // Attempt to extract string literals from both expressions.2464 const StringLiteral *LeftStrLiteral =2465 getCStringLiteral(C, state, Left.Expression, LeftVal);2466 const StringLiteral *RightStrLiteral =2467 getCStringLiteral(C, state, Right.Expression, RightVal);2468 bool canComputeResult = false;2469 SVal resultVal = svalBuilder.conjureSymbolVal(Call, C.blockCount());2470 2471 if (LeftStrLiteral && RightStrLiteral) {2472 StringRef LeftStrRef = LeftStrLiteral->getString();2473 StringRef RightStrRef = RightStrLiteral->getString();2474 2475 if (IsBounded) {2476 // Get the max number of characters to compare.2477 const Expr *lenExpr = Call.getArgExpr(2);2478 SVal lenVal = state->getSVal(lenExpr, LCtx);2479 2480 // If the length is known, we can get the right substrings.2481 if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) {2482 // Create substrings of each to compare the prefix.2483 LeftStrRef = LeftStrRef.substr(0, (size_t)len->getZExtValue());2484 RightStrRef = RightStrRef.substr(0, (size_t)len->getZExtValue());2485 canComputeResult = true;2486 }2487 } else {2488 // This is a normal, unbounded strcmp.2489 canComputeResult = true;2490 }2491 2492 if (canComputeResult) {2493 // Real strcmp stops at null characters.2494 size_t s1Term = LeftStrRef.find('\0');2495 if (s1Term != StringRef::npos)2496 LeftStrRef = LeftStrRef.substr(0, s1Term);2497 2498 size_t s2Term = RightStrRef.find('\0');2499 if (s2Term != StringRef::npos)2500 RightStrRef = RightStrRef.substr(0, s2Term);2501 2502 // Use StringRef's comparison methods to compute the actual result.2503 int compareRes = IgnoreCase ? LeftStrRef.compare_insensitive(RightStrRef)2504 : LeftStrRef.compare(RightStrRef);2505 2506 // The strcmp function returns an integer greater than, equal to, or less2507 // than zero, [c11, p7.24.4.2].2508 if (compareRes == 0) {2509 resultVal = svalBuilder.makeIntVal(compareRes, Call.getResultType());2510 }2511 else {2512 DefinedSVal zeroVal = svalBuilder.makeIntVal(0, Call.getResultType());2513 // Constrain strcmp's result range based on the result of StringRef's2514 // comparison methods.2515 BinaryOperatorKind op = (compareRes > 0) ? BO_GT : BO_LT;2516 SVal compareWithZero =2517 svalBuilder.evalBinOp(state, op, resultVal, zeroVal,2518 svalBuilder.getConditionType());2519 DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>();2520 state = state->assume(compareWithZeroVal, true);2521 }2522 }2523 }2524 2525 state = state->BindExpr(Call.getOriginExpr(), LCtx, resultVal);2526 2527 // Record this as a possible path.2528 C.addTransition(state);2529}2530 2531void CStringChecker::evalStrsep(CheckerContext &C,2532 const CallEvent &Call) const {2533 // char *strsep(char **stringp, const char *delim);2534 // Verify whether the search string parameter matches the return type.2535 SourceArgExpr SearchStrPtr = {{Call.getArgExpr(0), 0}};2536 2537 QualType CharPtrTy = SearchStrPtr.Expression->getType()->getPointeeType();2538 if (CharPtrTy.isNull() || Call.getResultType().getUnqualifiedType() !=2539 CharPtrTy.getUnqualifiedType())2540 return;2541 2542 CurrentFunctionDescription = "strsep()";2543 ProgramStateRef State = C.getState();2544 const LocationContext *LCtx = C.getLocationContext();2545 2546 // Check that the search string pointer is non-null (though it may point to2547 // a null string).2548 SVal SearchStrVal = State->getSVal(SearchStrPtr.Expression, LCtx);2549 State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);2550 if (!State)2551 return;2552 2553 // Check that the delimiter string is non-null.2554 AnyArgExpr DelimStr = {Call.getArgExpr(1), 1};2555 SVal DelimStrVal = State->getSVal(DelimStr.Expression, LCtx);2556 State = checkNonNull(C, State, DelimStr, DelimStrVal);2557 if (!State)2558 return;2559 2560 SValBuilder &SVB = C.getSValBuilder();2561 SVal Result;2562 if (std::optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {2563 // Get the current value of the search string pointer, as a char*.2564 Result = State->getSVal(*SearchStrLoc, CharPtrTy);2565 2566 // Invalidate the search string, representing the change of one delimiter2567 // character to NUL.2568 // As the replacement never overflows, do not invalidate its super region.2569 State = invalidateDestinationBufferNeverOverflows(2570 C, State, Call.getCFGElementRef(), Result);2571 2572 // Overwrite the search string pointer. The new value is either an address2573 // further along in the same string, or NULL if there are no more tokens.2574 State = State->bindLoc(*SearchStrLoc,2575 SVB.conjureSymbolVal(Call, C.blockCount(), getTag()),2576 LCtx);2577 } else {2578 assert(SearchStrVal.isUnknown());2579 // Conjure a symbolic value. It's the best we can do.2580 Result = SVB.conjureSymbolVal(Call, C.blockCount());2581 }2582 2583 // Set the return value, and finish.2584 State = State->BindExpr(Call.getOriginExpr(), LCtx, Result);2585 C.addTransition(State);2586}2587 2588// These should probably be moved into a C++ standard library checker.2589void CStringChecker::evalStdCopy(CheckerContext &C,2590 const CallEvent &Call) const {2591 evalStdCopyCommon(C, Call);2592}2593 2594void CStringChecker::evalStdCopyBackward(CheckerContext &C,2595 const CallEvent &Call) const {2596 evalStdCopyCommon(C, Call);2597}2598 2599void CStringChecker::evalStdCopyCommon(CheckerContext &C,2600 const CallEvent &Call) const {2601 if (!Call.getArgExpr(2)->getType()->isPointerType())2602 return;2603 2604 ProgramStateRef State = C.getState();2605 2606 const LocationContext *LCtx = C.getLocationContext();2607 2608 // template <class _InputIterator, class _OutputIterator>2609 // _OutputIterator2610 // copy(_InputIterator __first, _InputIterator __last,2611 // _OutputIterator __result)2612 2613 // Invalidate the destination buffer2614 const Expr *Dst = Call.getArgExpr(2);2615 SVal DstVal = State->getSVal(Dst, LCtx);2616 // FIXME: As we do not know how many items are copied, we also invalidate the2617 // super region containing the target location.2618 State = invalidateDestinationBufferAlwaysEscapeSuperRegion(2619 C, State, Call.getCFGElementRef(), DstVal);2620 2621 SValBuilder &SVB = C.getSValBuilder();2622 2623 SVal ResultVal = SVB.conjureSymbolVal(Call, C.blockCount());2624 State = State->BindExpr(Call.getOriginExpr(), LCtx, ResultVal);2625 2626 C.addTransition(State);2627}2628 2629void CStringChecker::evalMemset(CheckerContext &C,2630 const CallEvent &Call) const {2631 // void *memset(void *s, int c, size_t n);2632 CurrentFunctionDescription = "memory set function";2633 2634 DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};2635 AnyArgExpr CharE = {Call.getArgExpr(1), 1};2636 SizeArgExpr Size = {{Call.getArgExpr(2), 2}};2637 2638 ProgramStateRef State = C.getState();2639 2640 // See if the size argument is zero.2641 const LocationContext *LCtx = C.getLocationContext();2642 SVal SizeVal = C.getSVal(Size.Expression);2643 QualType SizeTy = Size.Expression->getType();2644 2645 ProgramStateRef ZeroSize, NonZeroSize;2646 std::tie(ZeroSize, NonZeroSize) = assumeZero(C, State, SizeVal, SizeTy);2647 2648 // Get the value of the memory area.2649 SVal BufferPtrVal = C.getSVal(Buffer.Expression);2650 2651 // If the size is zero, there won't be any actual memory access, so2652 // just bind the return value to the buffer and return.2653 if (ZeroSize && !NonZeroSize) {2654 ZeroSize = ZeroSize->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);2655 C.addTransition(ZeroSize);2656 return;2657 }2658 2659 // Ensure the memory area is not null.2660 // If it is NULL there will be a NULL pointer dereference.2661 State = checkNonNull(C, NonZeroSize, Buffer, BufferPtrVal);2662 if (!State)2663 return;2664 2665 State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);2666 if (!State)2667 return;2668 2669 // According to the values of the arguments, bind the value of the second2670 // argument to the destination buffer and set string length, or just2671 // invalidate the destination buffer.2672 if (!memsetAux(Buffer.Expression, Call.getCFGElementRef(),2673 C.getSVal(CharE.Expression), Size.Expression, C, State))2674 return;2675 2676 State = State->BindExpr(Call.getOriginExpr(), LCtx, BufferPtrVal);2677 C.addTransition(State);2678}2679 2680void CStringChecker::evalBzero(CheckerContext &C, const CallEvent &Call) const {2681 CurrentFunctionDescription = "memory clearance function";2682 2683 DestinationArgExpr Buffer = {{Call.getArgExpr(0), 0}};2684 SizeArgExpr Size = {{Call.getArgExpr(1), 1}};2685 SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);2686 2687 ProgramStateRef State = C.getState();2688 2689 // See if the size argument is zero.2690 SVal SizeVal = C.getSVal(Size.Expression);2691 QualType SizeTy = Size.Expression->getType();2692 2693 ProgramStateRef StateZeroSize, StateNonZeroSize;2694 std::tie(StateZeroSize, StateNonZeroSize) =2695 assumeZero(C, State, SizeVal, SizeTy);2696 2697 // If the size is zero, there won't be any actual memory access,2698 // In this case we just return.2699 if (StateZeroSize && !StateNonZeroSize) {2700 C.addTransition(StateZeroSize);2701 return;2702 }2703 2704 // Get the value of the memory area.2705 SVal MemVal = C.getSVal(Buffer.Expression);2706 2707 // Ensure the memory area is not null.2708 // If it is NULL there will be a NULL pointer dereference.2709 State = checkNonNull(C, StateNonZeroSize, Buffer, MemVal);2710 if (!State)2711 return;2712 2713 State = CheckBufferAccess(C, State, Buffer, Size, AccessKind::write);2714 if (!State)2715 return;2716 2717 if (!memsetAux(Buffer.Expression, Call.getCFGElementRef(), Zero,2718 Size.Expression, C, State))2719 return;2720 2721 C.addTransition(State);2722}2723 2724void CStringChecker::evalSprintf(CheckerContext &C,2725 const CallEvent &Call) const {2726 CurrentFunctionDescription = "'sprintf'";2727 evalSprintfCommon(C, Call, /* IsBounded = */ false);2728}2729 2730void CStringChecker::evalSnprintf(CheckerContext &C,2731 const CallEvent &Call) const {2732 CurrentFunctionDescription = "'snprintf'";2733 evalSprintfCommon(C, Call, /* IsBounded = */ true);2734}2735 2736void CStringChecker::evalSprintfCommon(CheckerContext &C, const CallEvent &Call,2737 bool IsBounded) const {2738 ProgramStateRef State = C.getState();2739 const auto *CE = cast<CallExpr>(Call.getOriginExpr());2740 DestinationArgExpr Dest = {{Call.getArgExpr(0), 0}};2741 2742 const auto NumParams = Call.parameters().size();2743 if (CE->getNumArgs() < NumParams) {2744 // This is an invalid call, let's just ignore it.2745 return;2746 }2747 2748 const auto AllArguments =2749 llvm::make_range(CE->getArgs(), CE->getArgs() + CE->getNumArgs());2750 const auto VariadicArguments = drop_begin(enumerate(AllArguments), NumParams);2751 2752 for (const auto &[ArgIdx, ArgExpr] : VariadicArguments) {2753 // We consider only string buffers2754 if (const QualType type = ArgExpr->getType();2755 !type->isAnyPointerType() ||2756 !type->getPointeeType()->isAnyCharacterType())2757 continue;2758 SourceArgExpr Source = {{ArgExpr, unsigned(ArgIdx)}};2759 2760 // Ensure the buffers do not overlap.2761 SizeArgExpr SrcExprAsSizeDummy = {2762 {Source.Expression, Source.ArgumentIndex}};2763 State = CheckOverlap(2764 C, State,2765 (IsBounded ? SizeArgExpr{{Call.getArgExpr(1), 1}} : SrcExprAsSizeDummy),2766 Dest, Source);2767 if (!State)2768 return;2769 }2770 2771 C.addTransition(State);2772}2773 2774//===----------------------------------------------------------------------===//2775// The driver method, and other Checker callbacks.2776//===----------------------------------------------------------------------===//2777 2778CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,2779 CheckerContext &C) const {2780 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());2781 if (!CE)2782 return nullptr;2783 2784 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());2785 if (!FD)2786 return nullptr;2787 2788 if (StdCopy.matches(Call))2789 return &CStringChecker::evalStdCopy;2790 if (StdCopyBackward.matches(Call))2791 return &CStringChecker::evalStdCopyBackward;2792 2793 // Pro-actively check that argument types are safe to do arithmetic upon.2794 // We do not want to crash if someone accidentally passes a structure2795 // into, say, a C++ overload of any of these functions. We could not check2796 // that for std::copy because they may have arguments of other types.2797 for (auto I : CE->arguments()) {2798 QualType T = I->getType();2799 if (!T->isIntegralOrEnumerationType() && !T->isPointerType())2800 return nullptr;2801 }2802 2803 const FnCheck *Callback = Callbacks.lookup(Call);2804 if (Callback)2805 return *Callback;2806 2807 return nullptr;2808}2809 2810bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {2811 FnCheck Callback = identifyCall(Call, C);2812 2813 // If the callee isn't a string function, let another checker handle it.2814 if (!Callback)2815 return false;2816 2817 // Check and evaluate the call.2818 assert(isa<CallExpr>(Call.getOriginExpr()));2819 Callback(this, C, Call);2820 2821 // If the evaluate call resulted in no change, chain to the next eval call2822 // handler.2823 // Note, the custom CString evaluation calls assume that basic safety2824 // properties are held. However, if the user chooses to turn off some of these2825 // checks, we ignore the issues and leave the call evaluation to a generic2826 // handler.2827 return C.isDifferent();2828}2829 2830void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {2831 // Record string length for char a[] = "abc";2832 ProgramStateRef state = C.getState();2833 2834 for (const auto *I : DS->decls()) {2835 const VarDecl *D = dyn_cast<VarDecl>(I);2836 if (!D)2837 continue;2838 2839 // FIXME: Handle array fields of structs.2840 if (!D->getType()->isArrayType())2841 continue;2842 2843 const Expr *Init = D->getInit();2844 if (!Init)2845 continue;2846 if (!isa<StringLiteral>(Init))2847 continue;2848 2849 Loc VarLoc = state->getLValue(D, C.getLocationContext());2850 const MemRegion *MR = VarLoc.getAsRegion();2851 if (!MR)2852 continue;2853 2854 SVal StrVal = C.getSVal(Init);2855 assert(StrVal.isValid() && "Initializer string is unknown or undefined");2856 DefinedOrUnknownSVal strLength =2857 getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();2858 2859 state = state->set<CStringLength>(MR, strLength);2860 }2861 2862 C.addTransition(state);2863}2864 2865ProgramStateRef2866CStringChecker::checkRegionChanges(ProgramStateRef state,2867 const InvalidatedSymbols *,2868 ArrayRef<const MemRegion *> ExplicitRegions,2869 ArrayRef<const MemRegion *> Regions,2870 const LocationContext *LCtx,2871 const CallEvent *Call) const {2872 CStringLengthTy Entries = state->get<CStringLength>();2873 if (Entries.isEmpty())2874 return state;2875 2876 llvm::SmallPtrSet<const MemRegion *, 8> Invalidated;2877 llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;2878 2879 // First build sets for the changed regions and their super-regions.2880 for (const MemRegion *MR : Regions) {2881 Invalidated.insert(MR);2882 2883 SuperRegions.insert(MR);2884 while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) {2885 MR = SR->getSuperRegion();2886 SuperRegions.insert(MR);2887 }2888 }2889 2890 CStringLengthTy::Factory &F = state->get_context<CStringLength>();2891 2892 // Then loop over the entries in the current state.2893 for (const MemRegion *MR : llvm::make_first_range(Entries)) {2894 // Is this entry for a super-region of a changed region?2895 if (SuperRegions.count(MR)) {2896 Entries = F.remove(Entries, MR);2897 continue;2898 }2899 2900 // Is this entry for a sub-region of a changed region?2901 const MemRegion *Super = MR;2902 while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) {2903 Super = SR->getSuperRegion();2904 if (Invalidated.count(Super)) {2905 Entries = F.remove(Entries, MR);2906 break;2907 }2908 }2909 }2910 2911 return state->set<CStringLength>(Entries);2912}2913 2914void CStringChecker::checkLiveSymbols(ProgramStateRef state,2915 SymbolReaper &SR) const {2916 // Mark all symbols in our string length map as valid.2917 CStringLengthTy Entries = state->get<CStringLength>();2918 2919 for (SVal Len : llvm::make_second_range(Entries)) {2920 for (SymbolRef Sym : Len.symbols())2921 SR.markInUse(Sym);2922 }2923}2924 2925void CStringChecker::checkDeadSymbols(SymbolReaper &SR,2926 CheckerContext &C) const {2927 ProgramStateRef state = C.getState();2928 CStringLengthTy Entries = state->get<CStringLength>();2929 if (Entries.isEmpty())2930 return;2931 2932 CStringLengthTy::Factory &F = state->get_context<CStringLength>();2933 for (auto [Reg, Len] : Entries) {2934 if (SymbolRef Sym = Len.getAsSymbol()) {2935 if (SR.isDead(Sym))2936 Entries = F.remove(Entries, Reg);2937 }2938 }2939 2940 state = state->set<CStringLength>(Entries);2941 C.addTransition(state);2942}2943 2944void ento::registerCStringModeling(CheckerManager &Mgr) {2945 // Other checker relies on the modeling implemented in this checker family,2946 // so this "modeling checker" can register the 'CStringChecker' backend for2947 // its callbacks without enabling any of its frontends.2948 Mgr.getChecker<CStringChecker>();2949}2950 2951bool ento::shouldRegisterCStringModeling(const CheckerManager &) {2952 return true;2953}2954 2955#define REGISTER_CHECKER(NAME) \2956 void ento::registerCString##NAME(CheckerManager &Mgr) { \2957 Mgr.getChecker<CStringChecker>()->NAME.enable(Mgr); \2958 } \2959 \2960 bool ento::shouldRegisterCString##NAME(const CheckerManager &) { \2961 return true; \2962 }2963 2964REGISTER_CHECKER(NullArg)2965REGISTER_CHECKER(OutOfBounds)2966REGISTER_CHECKER(BufferOverlap)2967REGISTER_CHECKER(NotNullTerm)2968REGISTER_CHECKER(UninitializedRead)2969