diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 84 |
1 files changed, 61 insertions, 23 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index aa1ca6f..c3736d7 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -141,8 +141,9 @@ public: SVal val) const; static ProgramStateRef InvalidateBuffer(CheckerContext &C, - ProgramStateRef state, - const Expr *Ex, SVal V); + ProgramStateRef state, + const Expr *Ex, SVal V, + bool IsSourceBuffer); static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); @@ -231,7 +232,7 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, return NULL; if (!BT_Null) - BT_Null.reset(new BuiltinBug("Unix API", + BT_Null.reset(new BuiltinBug(categories::UnixAPI, "Null pointer argument in call to byte string function")); SmallString<80> buf; @@ -525,7 +526,7 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, return; if (!BT_Overlap) - BT_Overlap.reset(new BugType("Unix API", "Improper arguments")); + BT_Overlap.reset(new BugType(categories::UnixAPI, "Improper arguments")); // Generate a report for this bug. BugReport *report = @@ -661,7 +662,7 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, if (Recorded) return *Recorded; } - + // Otherwise, get a new symbol and update the state. SValBuilder &svalBuilder = C.getSValBuilder(); QualType sizeTy = svalBuilder.getContext().getSizeType(); @@ -669,8 +670,21 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, MR, Ex, sizeTy, C.blockCount()); - if (!hypothetical) + if (!hypothetical) { + if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { + // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 + BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); + const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); + llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4); + const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt, + fourInt); + NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt); + SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, + maxLength, sizeTy); + state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true); + } state = state->set<CStringLength>(MR, strLength); + } return strLength; } @@ -689,7 +703,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug("Unix API", + BT_NotCString.reset(new BuiltinBug(categories::UnixAPI, "Argument is not a null-terminated string.")); SmallString<120> buf; @@ -749,7 +763,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, if (ExplodedNode *N = C.addTransition(state)) { if (!BT_NotCString) - BT_NotCString.reset(new BuiltinBug("Unix API", + BT_NotCString.reset(new BuiltinBug(categories::UnixAPI, "Argument is not a null-terminated string.")); SmallString<120> buf; @@ -796,8 +810,9 @@ const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, } ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, - ProgramStateRef state, - const Expr *E, SVal V) { + ProgramStateRef state, + const Expr *E, SVal V, + bool IsSourceBuffer) { Optional<Loc> L = V.getAs<Loc>(); if (!L) return state; @@ -817,8 +832,20 @@ ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, // Invalidate this region. const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - return state->invalidateRegions(R, E, C.blockCount(), LCtx, - /*CausesPointerEscape*/ false); + + bool CausesPointerEscape = false; + RegionAndSymbolInvalidationTraits ITraits; + // Invalidate and escape only indirect regions accessible through the source + // buffer. + if (IsSourceBuffer) { + ITraits.setTrait(R, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + CausesPointerEscape = true; + } + + return state->invalidateRegions(R, E, C.blockCount(), LCtx, + CausesPointerEscape, 0, 0, &ITraits); } // If we have a non-region value by chance, just remove the binding. @@ -955,13 +982,20 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, state = state->BindExpr(CE, LCtx, destVal); } - // Invalidate the destination. + // Invalidate the destination (regular invalidation without pointer-escaping + // the address of the top-level region). // FIXME: Even if we can't perfectly model the copy, we should see if we // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // copied region, but that's still an improvement over blank invalidation. - state = InvalidateBuffer(C, state, Dest, - state->getSVal(Dest, C.getLocationContext())); + state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest), + /*IsSourceBuffer*/false); + + // Invalidate the source (const-invalidation without const-pointer-escaping + // the address of the top-level region). + state = InvalidateBuffer(C, state, Source, C.getSVal(Source), + /*IsSourceBuffer*/true); + C.addTransition(state); } } @@ -1564,13 +1598,19 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, Result = lastElement; } - // Invalidate the destination. This must happen before we set the C string - // length because invalidation will clear the length. + // Invalidate the destination (regular invalidation without pointer-escaping + // the address of the top-level region). This must happen before we set the + // C string length because invalidation will clear the length. // FIXME: Even if we can't perfectly model the copy, we should see if we // can use LazyCompoundVals to copy the source values into the destination. // This would probably remove any existing bindings past the end of the // string, but that's still an improvement over blank invalidation. - state = InvalidateBuffer(C, state, Dst, *dstRegVal); + state = InvalidateBuffer(C, state, Dst, *dstRegVal, + /*IsSourceBuffer*/false); + + // Invalidate the source (const-invalidation without const-pointer-escaping + // the address of the top-level region). + state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true); // Set the C string length of the destination, if we know it. if (isBounded && !isAppending) { @@ -1792,7 +1832,8 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { // Invalidate the search string, representing the change of one delimiter // character to NUL. - State = InvalidateBuffer(C, State, SearchStrPtr, Result); + State = InvalidateBuffer(C, State, SearchStrPtr, Result, + /*IsSourceBuffer*/false); // Overwrite the search string pointer. The new value is either an address // further along in the same string, or NULL if there are no more tokens. @@ -2018,10 +2059,7 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR, #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) {\ - static CStringChecker *TheChecker = 0; \ - if (TheChecker == 0) \ - TheChecker = mgr.registerChecker<CStringChecker>(); \ - TheChecker->Filter.Check##name = true; \ + mgr.registerChecker<CStringChecker>()->Filter.Check##name = true; \ } REGISTER_CHECKER(CStringNullArg) |