diff options
author | Henry Wong <movietravelcode@outlook.com> | 2018-05-16 12:37:53 +0000 |
---|---|---|
committer | Henry Wong <movietravelcode@outlook.com> | 2018-05-16 12:37:53 +0000 |
commit | afe62cdc4e411953f3ceea398ad50d9862323dd7 (patch) | |
tree | 742992b13b20774ae83e93552f17c4a78d4a67ca /clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | |
parent | 6c35f8f445b0627a7563b2c5c2d3fe3dc2fd6a06 (diff) | |
download | bcm5719-llvm-afe62cdc4e411953f3ceea398ad50d9862323dd7.tar.gz bcm5719-llvm-afe62cdc4e411953f3ceea398ad50d9862323dd7.zip |
[analyzer] Improve the modeling of memset().
Since there is no perfect way bind the non-zero value with the default binding, this patch only considers the case where buffer's offset is zero and the char value is 0. And according to the value for overwriting, decide how to update the string length.
Reviewers: dcoughlin, NoQ, xazax.hun, a.sidorin, george.karpenkov
Reviewed By: NoQ
Differential Revision: https://reviews.llvm.org/D44934
llvm-svn: 332463
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 102 |
1 files changed, 99 insertions, 3 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index f97c513f371..39db5861890 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -158,6 +158,10 @@ public: static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); + static bool memsetAux(const Expr *DstBuffer, const Expr *CharE, + const Expr *Size, CheckerContext &C, + ProgramStateRef &State); + // Re-usable checks ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef state, @@ -999,6 +1003,95 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, } } +bool CStringChecker::memsetAux(const Expr *DstBuffer, const Expr *CharE, + const Expr *Size, CheckerContext &C, + ProgramStateRef &State) { + SVal MemVal = C.getSVal(DstBuffer); + SVal CharVal = C.getSVal(CharE); + SVal SizeVal = C.getSVal(Size); + const MemRegion *MR = MemVal.getAsRegion(); + if (!MR) + return false; + + // We're about to model memset by producing a "default binding" in the Store. + // Our current implementation - RegionStore - doesn't support default bindings + // that don't cover the whole base region. So we should first get the offset + // and the base region to figure out whether the offset of buffer is 0. + RegionOffset Offset = MR->getAsOffset(); + const MemRegion *BR = Offset.getRegion(); + + Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); + if (!SizeNL) + return false; + + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); + + // void *memset(void *dest, int ch, size_t count); + // For now we can only handle the case of offset is 0 and concrete char value. + if (Offset.isValid() && !Offset.hasSymbolicOffset() && + Offset.getOffset() == 0) { + // Get the base region's extent. + auto *SubReg = cast<SubRegion>(BR); + DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder); + + ProgramStateRef StateWholeReg, StateNotWholeReg; + std::tie(StateWholeReg, StateNotWholeReg) = + State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL)); + + // With the semantic of 'memset()', we should convert the CharVal to + // unsigned char. + CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy); + + ProgramStateRef StateNullChar, StateNonNullChar; + std::tie(StateNullChar, StateNonNullChar) = + assumeZero(C, State, CharVal, Ctx.UnsignedCharTy); + + if (StateWholeReg && !StateNotWholeReg && StateNullChar && + !StateNonNullChar) { + // If the 'memset()' acts on the whole region of destination buffer and + // the value of the second argument of 'memset()' is zero, bind the second + // argument's value to the destination buffer with 'default binding'. + // FIXME: Since there is no perfect way to bind the non-zero character, we + // can only deal with zero value here. In the future, we need to deal with + // the binding of non-zero value in the case of whole region. + State = State->bindDefaultZero(svalBuilder.makeLoc(BR), + C.getLocationContext()); + } else { + // If the destination buffer's extent is not equal to the value of + // third argument, just invalidate buffer. + State = InvalidateBuffer(C, State, DstBuffer, MemVal, + /*IsSourceBuffer*/ false, Size); + } + + if (StateNullChar && !StateNonNullChar) { + // If the value of the second argument of 'memset()' is zero, set the + // string length of destination buffer to 0 directly. + State = setCStringLength(State, MR, + svalBuilder.makeZeroVal(Ctx.getSizeType())); + } else if (!StateNullChar && StateNonNullChar) { + SVal NewStrLen = svalBuilder.getMetadataSymbolVal( + CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(), + C.getLocationContext(), C.blockCount()); + + // If the value of second argument is not zero, then the string length + // is at least the size argument. + SVal NewStrLenGESize = svalBuilder.evalBinOp( + State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType()); + + State = setCStringLength( + State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true), + MR, NewStrLen); + } + } else { + // If the offset is not zero and char value is not concrete, we can do + // nothing but invalidate the buffer. + State = InvalidateBuffer(C, State, DstBuffer, MemVal, + /*IsSourceBuffer*/ false, Size); + } + return true; +} + //===----------------------------------------------------------------------===// // evaluation of individual function calls. //===----------------------------------------------------------------------===// @@ -2048,6 +2141,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { CurrentFunctionDescription = "memory set function"; const Expr *Mem = CE->getArg(0); + const Expr *CharE = CE->getArg(1); const Expr *Size = CE->getArg(2); ProgramStateRef State = C.getState(); @@ -2080,9 +2174,11 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { State = CheckBufferAccess(C, State, Size, Mem); if (!State) return; - State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), - /*IsSourceBuffer*/false, Size); - if (!State) + + // According to the values of the arguments, bind the value of the second + // argument to the destination buffer and set string length, or just + // invalidate the destination buffer. + if (!memsetAux(Mem, CharE, Size, C, State)) return; State = State->BindExpr(CE, LCtx, MemVal); |