From 6cd9d89a3ce21c320eadefd8d5c9774db2a07151 Mon Sep 17 00:00:00 2001 From: Leslie Zhai Date: Tue, 20 Jun 2017 06:41:06 +0000 Subject: [PATCH] [analyzer] Check NULL pointer dereference issue for memset function Reviewers: dcoughlin, zaks.anna, NoQ, danielmarjamaki Reviewed By: NoQ, danielmarjamaki Differential Revision: https://reviews.llvm.org/D31868 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@305773 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/CStringChecker.cpp | 51 ++++++++++++++++ test/Analysis/null-deref-ps-region.c | 61 ++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 32e3ce9270..77c24629d7 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -120,6 +120,7 @@ public: 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,54 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C, 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 *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; + } + + // 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; + + State = CheckBufferAccess(C, State, Size, Mem); + if (!State) + return; + State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), + /*IsSourceBuffer*/false, Size); + if (!State) + return; + + State = State->BindExpr(CE, LCtx, MemVal); + C.addTransition(State); +} + static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { IdentifierInfo *II = FD->getIdentifier(); if (!II) @@ -2032,6 +2081,8 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { 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")) diff --git a/test/Analysis/null-deref-ps-region.c b/test/Analysis/null-deref-ps-region.c index 6ef99ae473..c46ca6c52a 100644 --- a/test/Analysis/null-deref-ps-region.c +++ b/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 @@ void f14(int *a) { i = *p; // no-warning } } + +void foo() { + int *x = malloc(sizeof(int)); + memset(x, 0, sizeof(int)); + int n = 1 / *x; // FIXME: no-warning + free(x); +} + +void bar() { + int *x = malloc(sizeof(int)); + memset(x, 0, 1); + int n = 1 / *x; // no-warning + free(x); +} + +void testConcreteNull() { + int *x = 0; + memset(x, 0, 1); // expected-warning {{Null pointer argument in call to memory set function}} +} + +void testStackArray() { + char buf[13]; + memset(buf, 0, 1); // no-warning +} + +void testHeapSymbol() { + char *buf = (char *)malloc(13); + memset(buf, 0, 1); // no-warning + free(buf); +} + +void testStackArrayOutOfBound() { + char buf[1]; + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} +} + +void testHeapSymbolOutOfBound() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1024); // expected-warning {{Memory set function accesses out-of-bound array element}} + free(buf); +} + +void testStackArraySameSize() { + char buf[1]; + memset(buf, 0, sizeof(buf)); // no-warning +} + +void testHeapSymbolSameSize() { + char *buf = (char *)malloc(1); + memset(buf, 0, 1); // no-warning + free(buf); +} -- 2.40.0