From 37a22ea063625e28c931eae38653356d9849ae01 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 11 Dec 2018 18:57:07 +0000 Subject: [PATCH] [analyzer][CStringChecker] evaluate explicit_bzero - explicit_bzero has limited scope/usage only for security/crypto purposes but is non-optimisable version of memset/0 and bzero. - explicit_memset has similar signature and semantics as memset but is also a non-optimisable version. Reviewers: NoQ Reviewed By: NoQ Differential Revision: https://reviews.llvm.org/D54592 llvm-svn: 348884 --- .../lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 60 ++++++++++++++++++++-- clang/test/Analysis/string.c | 56 +++++++++++++++++++- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 0a1a108..ed68df9 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -124,6 +124,7 @@ public: void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; void evalMemset(CheckerContext &C, const CallExpr *CE) const; + void evalBzero(CheckerContext &C, const CallExpr *CE) const; // Utility methods std::pair @@ -158,7 +159,7 @@ public: static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, const MemRegion *MR); - static bool memsetAux(const Expr *DstBuffer, const Expr *CharE, + static bool memsetAux(const Expr *DstBuffer, SVal CharE, const Expr *Size, CheckerContext &C, ProgramStateRef &State); @@ -1005,11 +1006,10 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, } } -bool CStringChecker::memsetAux(const Expr *DstBuffer, const Expr *CharE, +bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, 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) @@ -2184,13 +2184,59 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { // 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)) + if (!memsetAux(Mem, C.getSVal(CharE), Size, C, State)) return; State = State->BindExpr(CE, LCtx, MemVal); C.addTransition(State); } +void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() != 2) + return; + + CurrentFunctionDescription = "memory clearance function"; + + const Expr *Mem = CE->getArg(0); + const Expr *Size = CE->getArg(1); + SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy); + + ProgramStateRef State = C.getState(); + + // See if the size argument is zero. + SVal SizeVal = C.getSVal(Size); + QualType SizeTy = Size->getType(); + + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, State, SizeVal, SizeTy); + + // If the size is zero, there won't be any actual memory access, + // In this case we just return. + if (StateZeroSize && !StateNonZeroSize) { + C.addTransition(StateZeroSize); + return; + } + + // Get the value of the memory area. + SVal MemVal = C.getSVal(Mem); + + // 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; + + if (!memsetAux(Mem, Zero, Size, C, State)) + return; + + C.addTransition(State); +} + static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { IdentifierInfo *II = FD->getIdentifier(); if (!II) @@ -2224,7 +2270,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")) + else if (C.isCLibraryFunction(FDecl, "memset") || + C.isCLibraryFunction(FDecl, "explicit_memset")) evalFunction = &CStringChecker::evalMemset; else if (C.isCLibraryFunction(FDecl, "strcpy")) evalFunction = &CStringChecker::evalStrcpy; @@ -2262,6 +2309,9 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { evalFunction = &CStringChecker::evalStdCopy; else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) evalFunction = &CStringChecker::evalStdCopyBackward; + else if (C.isCLibraryFunction(FDecl, "bzero") || + C.isCLibraryFunction(FDecl, "explicit_bzero")) + evalFunction = &CStringChecker::evalBzero; // If the callee isn't a string function, let another checker handle it. if (!evalFunction) diff --git a/clang/test/Analysis/string.c b/clang/test/Analysis/string.c index a3a6a26..024e224 100644 --- a/clang/test/Analysis/string.c +++ b/clang/test/Analysis/string.c @@ -1184,11 +1184,14 @@ void strsep_changes_input_string() { } //===----------------------------------------------------------------------=== -// memset() +// memset() / explicit_bzero() / bzero() //===----------------------------------------------------------------------=== void *memset(void *dest, int ch, size_t count); +void bzero(void *dst, size_t count); +void explicit_bzero(void *dest, size_t count); + void *malloc(size_t size); void free(void *); @@ -1383,6 +1386,57 @@ void memset26_upper_UCHAR_MAX() { clang_analyzer_eval(array[4] == 0); // expected-warning{{TRUE}} } +void bzero1_null() { + char *a = NULL; + + bzero(a, 10); // expected-warning{{Null pointer argument in call to memory clearance function}} +} + +void bzero2_char_array_null() { + char str[] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + bzero(str, 2); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} +} + +void bzero3_char_ptr_null() { + char *str = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + bzero(str + 2, 2); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{FALSE}} +} + +void explicit_bzero1_null() { + char *a = NULL; + + explicit_bzero(a, 10); // expected-warning{{Null pointer argument in call to memory clearance function}} +} + +void explicit_bzero2_clear_mypassword() { + char passwd[7] = "passwd"; + + explicit_bzero(passwd, sizeof(passwd)); // no-warning + + clang_analyzer_eval(strlen(passwd) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(passwd[0] == '\0'); // expected-warning{{TRUE}} +} + +void explicit_bzero3_out_ofbound() { + char *privkey = (char *)malloc(7); + const char newprivkey[10] = "mysafekey"; + + strcpy(privkey, "random"); + explicit_bzero(privkey, sizeof(newprivkey)); +#ifndef SUPPRESS_OUT_OF_BOUND + // expected-warning@-2 {{Memory clearance function accesses out-of-bound array element}} +#endif + clang_analyzer_eval(privkey[0] == '\0'); +#ifdef SUPPRESS_OUT_OF_BOUND + // expected-warning@-2 {{UNKNOWN}} +#endif + free(privkey); +} + //===----------------------------------------------------------------------=== // FIXMEs //===----------------------------------------------------------------------=== -- 2.7.4