diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2022-07-03 14:10:23 +0000 |
| commit | 145449b1e420787bb99721a429341fa6be3adfb6 (patch) | |
| tree | 1d56ae694a6de602e348dd80165cf881a36600ed /clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | |
| parent | ecbca9f5fb7d7613d2b94982c4825eb0d33d6842 (diff) | |
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 80 |
1 files changed, 57 insertions, 23 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 6955efe8e6c2..2e4c8e643698 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -80,7 +80,7 @@ class CStringChecker : public Checker< eval::Call, check::RegionChanges > { mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap, - BT_NotCString, BT_AdditionOverflow; + BT_NotCString, BT_AdditionOverflow, BT_UninitRead; mutable const char *CurrentFunctionDescription; @@ -88,15 +88,17 @@ public: /// The filter is used to filter out the diagnostics which are not enabled by /// the user. struct CStringChecksFilter { - DefaultBool CheckCStringNullArg; - DefaultBool CheckCStringOutOfBounds; - DefaultBool CheckCStringBufferOverlap; - DefaultBool CheckCStringNotNullTerm; + bool CheckCStringNullArg = false; + bool CheckCStringOutOfBounds = false; + bool CheckCStringBufferOverlap = false; + bool CheckCStringNotNullTerm = false; + bool CheckCStringUninitializedRead = false; CheckerNameRef CheckNameCStringNullArg; CheckerNameRef CheckNameCStringOutOfBounds; CheckerNameRef CheckNameCStringBufferOverlap; CheckerNameRef CheckNameCStringNotNullTerm; + CheckerNameRef CheckNameCStringUninitializedRead; }; CStringChecksFilter Filter; @@ -257,7 +259,8 @@ public: void emitNotCStringBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, StringRef WarningMsg) const; void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const; - + void emitUninitializedReadBug(CheckerContext &C, ProgramStateRef State, + const Expr *E) const; ProgramStateRef checkAdditionOverflow(CheckerContext &C, ProgramStateRef state, NonLoc left, @@ -352,8 +355,8 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, // Get the index of the accessed element. DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); - ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); - ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); + ProgramStateRef StInBound, StOutBound; + std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size); if (StOutBound && !StInBound) { // These checks are either enabled by the CString out-of-bounds checker // explicitly or implicitly by the Malloc checker. @@ -368,6 +371,15 @@ ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, return nullptr; } + // Ensure that we wouldn't read uninitialized value. + if (Access == AccessKind::read) { + if (Filter.CheckCStringUninitializedRead && + StInBound->getSVal(ER).isUndef()) { + emitUninitializedReadBug(C, StInBound, Buffer.Expression); + return nullptr; + } + } + // Array bound check succeeded. From this point forward the array bound // should always succeed. return StInBound; @@ -420,7 +432,6 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, SVal BufEnd = svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy); - State = CheckLocation(C, State, Buffer, BufEnd, Access); // If the buffer isn't large enough, abort. @@ -449,6 +460,11 @@ ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, ProgramStateRef stateTrue, stateFalse; + // Assume different address spaces cannot overlap. + if (First.Expression->getType()->getPointeeType().getAddressSpace() != + Second.Expression->getType()->getPointeeType().getAddressSpace()) + return state; + // Get the buffer values and make sure they're known locations. const LocationContext *LCtx = C.getLocationContext(); SVal firstVal = state->getSVal(First.Expression, LCtx); @@ -580,6 +596,26 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, } } +void CStringChecker::emitUninitializedReadBug(CheckerContext &C, + ProgramStateRef State, + const Expr *E) const { + if (ExplodedNode *N = C.generateErrorNode(State)) { + const char *Msg = + "Bytes string function accesses uninitialized/garbage values"; + if (!BT_UninitRead) + BT_UninitRead.reset( + new BuiltinBug(Filter.CheckNameCStringUninitializedRead, + "Accessing unitialized/garbage values", Msg)); + + BuiltinBug *BT = static_cast<BuiltinBug *>(BT_UninitRead.get()); + + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); + Report->addRange(E->getSourceRange()); + bugreporter::trackExpressionValue(N, E, *Report); + C.emitReport(std::move(Report)); + } +} + void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, StringRef WarningMsg) const { @@ -622,8 +658,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const { if (ExplodedNode *N = C.generateErrorNode(State)) { - if (!BT_NotCString) - BT_NotCString.reset( + if (!BT_AdditionOverflow) + BT_AdditionOverflow.reset( new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API", "Sum of expressions causes overflow.")); @@ -634,8 +670,8 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, "This expression will create a string whose length is too big to " "be represented as a size_t"; - auto Report = - std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT_AdditionOverflow, + WarningMsg, N); C.emitReport(std::move(Report)); } } @@ -660,7 +696,7 @@ ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); SVal maxMinusRight; - if (right.getAs<nonloc::ConcreteInt>()) { + if (isa<nonloc::ConcreteInt>(right)) { maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, sizeTy); } else { @@ -1010,23 +1046,20 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, case MemRegion::CXXThisRegionKind: case MemRegion::CXXTempObjectRegionKind: os << "a C++ temp object of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::NonParamVarRegionKind: - os << "a variable of type" - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a variable of type" << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::ParamVarRegionKind: - os << "a parameter of type" - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a parameter of type" << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::FieldRegionKind: - os << "a field of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + os << "a field of type " << cast<TypedValueRegion>(MR)->getValueType(); return true; case MemRegion::ObjCIvarRegionKind: os << "an instance variable of type " - << cast<TypedValueRegion>(MR)->getValueType().getAsString(); + << cast<TypedValueRegion>(MR)->getValueType(); return true; default: return false; @@ -1642,7 +1675,7 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // amountCopied = min (size - dstLen - 1 , srcLen) SVal freeSpace = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, *dstStrLengthNL, sizeTy); - if (!freeSpace.getAs<NonLoc>()) + if (!isa<NonLoc>(freeSpace)) return; freeSpace = svalBuilder.evalBinOp(state, BO_Sub, freeSpace, @@ -2460,3 +2493,4 @@ REGISTER_CHECKER(CStringNullArg) REGISTER_CHECKER(CStringOutOfBounds) REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) +REGISTER_CHECKER(CStringUninitializedRead) |
