Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -120,6 +120,7 @@ void evalStdCopy(CheckerContext &C, const CallExpr *CE) const; void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; + void evalMemset(CheckerContext &C, const CallExpr *CE) const; // Utility methods std::pair @@ -1999,6 +2000,79 @@ C.addTransition(State); } +void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() != 3) + return; + + CurrentFunctionDescription = "memory set function"; + + const Expr *Mem = CE->getArg(0); + const Expr *Const = CE->getArg(1); + const Expr *Size = CE->getArg(2); + ProgramStateRef State = C.getState(); + + // See if the size argument is zero. + const LocationContext *LCtx = C.getLocationContext(); + SVal SizeVal = State->getSVal(Size, LCtx); + QualType SizeTy = Size->getType(); + + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, State, SizeVal, SizeTy); + + // Get the value of the memory area. + SVal MemVal = State->getSVal(Mem, LCtx); + + // If the size is zero, there won't be any actual memory access, so + // just bind the return value to the Mem buffer and return. + if (StateZeroSize && !StateNonZeroSize) { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, MemVal); + C.addTransition(StateZeroSize); + return; + } + + if (!StateNonZeroSize) + return; + + // Ensure the memory area is not null. + // If it is NULL there will be a NULL pointer dereference. + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + if (!State) + return; + + SValBuilder &SB = C.getSValBuilder(); + // Check the region's extent is equal to the Size parameter. + SVal RetVal = SB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + const SymbolicRegion *R = + dyn_cast_or_null(RetVal.getAsRegion()); + if (!R) + return; + if (Optional DefinedSize = + SizeVal.getAs()) { + DefinedOrUnknownSVal Extent = R->getExtent(SB); + ProgramStateRef StateSameSize, StateNotSameSize; + std::tie(StateSameSize, StateNotSameSize) = + State->assume(SB.evalEQ(State, Extent, *DefinedSize)); + if (StateNotSameSize) { + State = CheckBufferAccess(C, State, Size, Mem); + if (!State) + return; + State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), + /*IsSourceBuffer*/false, Size); + if (!State) + return; + } + + if (StateSameSize) { + SVal ConstVal = State->getSVal(Const, LCtx); + State = State->BindExpr(CE, LCtx, RetVal); + // Actually bind the second argument value to the buffer. + State = State->bindDefault(RetVal, ConstVal, LCtx); + C.addTransition(State); + } + } +} + static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { IdentifierInfo *II = FD->getIdentifier(); if (!II) @@ -2032,6 +2106,8 @@ evalFunction = &CStringChecker::evalMemcmp; else if (C.isCLibraryFunction(FDecl, "memmove")) evalFunction = &CStringChecker::evalMemmove; + else if (C.isCLibraryFunction(FDecl, "memset")) + evalFunction = &CStringChecker::evalMemset; else if (C.isCLibraryFunction(FDecl, "strcpy")) evalFunction = &CStringChecker::evalStrcpy; else if (C.isCLibraryFunction(FDecl, "strncpy")) Index: test/Analysis/null-deref-ps-region.c =================================================================== --- test/Analysis/null-deref-ps-region.c +++ test/Analysis/null-deref-ps-region.c @@ -1,6 +1,11 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core -std=gnu99 -analyzer-store=region -verify %s -// expected-no-diagnostics +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.core,unix,alpha.unix -std=gnu99 -analyzer-store=region -verify %s +#include "Inputs/system-header-simulator.h" + +typedef __typeof(sizeof(int)) size_t; +void *memset(void *__s, int __c, size_t __n); +void *malloc(size_t __size); +void free(void *__ptr); // The store for 'a[1]' should not be removed mistakenly. SymbolicRegions may // also be live roots. @@ -13,3 +18,55 @@ i = *p; // no-warning } } + +void foo() { + int *x = malloc(sizeof(int)); + int *r = memset(x, 0, sizeof(int)); + int n = 1 / *r; // expected-warning {{Division by zero}} + free(x); +} + +void bar() { + int *x = malloc(sizeof(int)); + memset(x, 0, 1); + int n = 1 / *x; // no-warning + free(x); +} + +void f531() { + int *x = 0; + memset(x, 0, 1); // expected-warning {{Null pointer argument in call to memory set function}} +} + +void f61() { + char buf[13]; + memset(buf, 0, 1); // no-warning +} + +void f611() { + char *buf = (char *)malloc(13); + memset(buf, 0, 1); // no-warning + free(buf); +} + +void f66() { + char buf[1]; + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} +} + +void f666() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} + free(buf); +} + +void f77() { + char buf[1]; + memset(buf, 0, sizeof(buf)); // no-warning +} + +void f777() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1); // no-warning + free(buf); +}