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,63 @@ C.addTransition(State); } +void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + CurrentFunctionDescription = "memory set function"; + + const Expr *S = CE->getArg(0); + 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 pointed to by S. + SVal sVal = state->getSVal(S, LCtx); + + // If the size is zero, there won't be any actual memory access, so + // just bind the return value to the destination buffer and return. + if (stateZeroSize && !stateNonZeroSize) { + stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, sVal); + C.addTransition(stateZeroSize); + return; + } + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + + // Ensure the memory area pointed to by s is not null. + // If it is NULL there will be a NULL pointer dereference. + state = checkNonNull(C, state, S, sVal); + if (!state) + return; + + // Ensure the accesses are valid. + state = CheckBufferAccess(C, state, Size, S); + if (!state) + return; + + // Return the memory area pointed to by s buffer. + state = state->BindExpr(CE, LCtx, sVal); + + // Invalidate the memory area pointed to by s (regular invalidation without + // pointer-escaping the address of the top-level region). + state = InvalidateBuffer(C, state, S, C.getSVal(S), + /*IsSourceBuffer*/false, Size); + + C.addTransition(state); + } +} + static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { IdentifierInfo *II = FD->getIdentifier(); if (!II) @@ -2032,6 +2090,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,9 @@ -// 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); // The store for 'a[1]' should not be removed mistakenly. SymbolicRegions may // also be live roots. @@ -13,3 +16,18 @@ i = *p; // no-warning } } + +void f531() { + int *x = 0; + memset(x, 0, 1); // expected-warning {{Null pointer argument in call to memory set function}} +} + +void f66() { + char buf[1]; + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} +} + +void f77() { + char buf[1]; + memset(buf, 0, sizeof(buf)); // no-warning +}