[clang] 811b173 - [analyzer] Add InvalidPtrChecker
Zurab Tsinadze via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 4 08:13:28 PDT 2021
Author: Zurab Tsinadze
Date: 2021-10-04T17:08:34+02:00
New Revision: 811b1736d91b301f59fbaa148ff55c32f90a3947
URL: https://github.com/llvm/llvm-project/commit/811b1736d91b301f59fbaa148ff55c32f90a3947
DIFF: https://github.com/llvm/llvm-project/commit/811b1736d91b301f59fbaa148ff55c32f90a3947.diff
LOG: [analyzer] Add InvalidPtrChecker
This patch introduces a new checker: `alpha.security.cert.env.InvalidPtr`
Checker finds usage of invalidated pointers related to environment.
Based on the following SEI CERT Rules:
ENV34-C: https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
ENV31-C: https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
Reviewed By: martong
Differential Revision: https://reviews.llvm.org/D97699
Added:
clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
clang/test/Analysis/cert/env31-c.c
clang/test/Analysis/cert/env34-c-cert-examples.c
clang/test/Analysis/cert/env34-c.c
Modified:
clang/docs/analyzer/checkers.rst
clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Removed:
################################################################################
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 89190b54e5a51..a0c207b974678 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -2075,10 +2075,63 @@ Finds calls to the ``putenv`` function which pass a pointer to an automatic vari
if (retval < 0 || (size_t)retval >= sizeof(env)) {
/* Handle error */
}
-
+
return putenv(env); // putenv function should not be called with auto variables
}
-
+
+alpha.security.cert.env
+^^^^^^^^^^^^^^^^^^^^^^^
+
+SEI CERT checkers of `POSIX C coding rules <https://wiki.sei.cmu.edu/confluence/x/JdcxBQ>`_.
+
+.. _alpha-security-cert-env-InvalidPtr:
+
+alpha.security.cert.env.InvalidPtr
+"""""""""""""""""""""""""""
+
+Corresponds to SEI CERT Rules ENV31-C and ENV34-C.
+
+ENV31-C:
+Rule is about the possible problem with `main` function's third argument, environment pointer,
+"envp". When enviornment array is modified using some modification function
+such as putenv, setenv or others, It may happen that memory is reallocated,
+however "envp" is not updated to reflect the changes and points to old memory
+region.
+
+ENV34-C:
+Some functions return a pointer to a statically allocated buffer.
+Consequently, subsequent call of these functions will invalidate previous
+pointer. These functions include: getenv, localeconv, asctime, setlocale, strerror
+
+.. code-block:: c
+
+ int main(int argc, const char *argv[], const char *envp[]) {
+ if (setenv("MY_NEW_VAR", "new_value", 1) != 0) {
+ // setenv call may invalidate 'envp'
+ /* Handle error */
+ }
+ if (envp != NULL) {
+ for (size_t i = 0; envp[i] != NULL; ++i) {
+ puts(envp[i]);
+ // envp may no longer point to the current environment
+ // this program has unanticipated behavior, since envp
+ // does not reflect changes made by setenv function.
+ }
+ }
+ return 0;
+ }
+
+ void previous_call_invalidation() {
+ char *p, *pp;
+
+ p = getenv("VAR");
+ pp = getenv("VAR2");
+ // subsequent call to 'getenv' invalidated previous one
+
+ *p;
+ // dereferencing invalid pointer
+ }
+
.. _alpha-security-ArrayBound:
alpha.security.ArrayBound (C)
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 86513a3e541be..aac111982b235 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -73,6 +73,7 @@ def Taint : Package<"taint">, ParentPackage<SecurityAlpha>;
def CERT : Package<"cert">, ParentPackage<SecurityAlpha>;
def POS : Package<"pos">, ParentPackage<CERT>;
+def ENV : Package<"env">, ParentPackage<CERT>;
def Unix : Package<"unix">;
def UnixAlpha : Package<"unix">, ParentPackage<Alpha>;
@@ -947,6 +948,14 @@ let ParentPackage = POS in {
} // end "alpha.cert.pos"
+let ParentPackage = ENV in {
+
+ def InvalidPtrChecker : Checker<"InvalidPtr">,
+ HelpText<"Finds usages of possibly invalidated pointers">,
+ Documentation<HasDocumentation>;
+
+} // end "alpha.cert.env"
+
let ParentPackage = SecurityAlpha in {
def ArrayBoundChecker : Checker<"ArrayBound">,
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index eb4f301377325..e7d424ae91504 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -49,6 +49,7 @@ add_clang_library(clangStaticAnalyzerCheckers
IdenticalExprChecker.cpp
InnerPointerChecker.cpp
InvalidatedIteratorChecker.cpp
+ cert/InvalidPtrChecker.cpp
Iterator.cpp
IteratorModeling.cpp
IteratorRangeChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
new file mode 100644
index 0000000000000..18893c2fda15b
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
@@ -0,0 +1,279 @@
+//== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines InvalidPtrChecker which finds usages of possibly
+// invalidated pointer.
+// CERT SEI Rules ENV31-C and ENV34-C
+// For more information see:
+// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
+// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class InvalidPtrChecker
+ : public Checker<check::Location, check::BeginFunction, check::PostCall> {
+private:
+ BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
+
+ void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
+
+ using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV31-C
+ const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
+ {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ };
+
+ void postPreviousReturnInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV34-C
+ const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
+ {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"setlocale", 2},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"localeconv", 0},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ };
+
+public:
+ // Obtain the environment pointer from 'main()' (if present).
+ void checkBeginFunction(CheckerContext &C) const;
+
+ // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+ // pointer from 'main()'
+ // Handle functions in PreviousCallInvalidatingFunctions.
+ // Also, check if invalidated region is passed to a
+ // conservatively evaluated function call as an argument.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // Check if invalidated region is being dereferenced.
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+};
+
+} // namespace
+
+// Set of memory regions that were invalidated
+REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
+
+// Stores the region of the environment pointer of 'main' (if present).
+// Note: This pointer has type 'const MemRegion *', however the trait is only
+// specialized to 'const void*' and 'void*'
+REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
+
+// Stores key-value pairs, where key is function declaration and value is
+// pointer to memory region returned by previous call of this function
+REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
+ const MemRegion *)
+
+void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ StringRef FunctionName = Call.getCalleeIdentifier()->getName();
+ ProgramStateRef State = C.getState();
+ const auto *Reg = State->get<EnvPtrRegion>();
+ if (!Reg)
+ return;
+ const auto *SymbolicEnvPtrRegion =
+ reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
+
+ State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
+
+ const NoteTag *Note =
+ C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(SymbolicEnvPtrRegion))
+ return;
+ Out << '\'' << FunctionName
+ << "' call may invalidate the environment parameter of 'main'";
+ });
+
+ C.addTransition(State, Note);
+}
+
+void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
+ const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ const NoteTag *Note = nullptr;
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ // Invalidate the region of the previously returned pointer - if there was
+ // one.
+ if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
+ const MemRegion *PrevReg = *Reg;
+ State = State->add<InvalidMemoryRegions>(PrevReg);
+ Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(PrevReg))
+ return;
+ Out << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << "' call may invalidate the the result of the previous " << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << '\'';
+ });
+ }
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+
+ // Function call will return a pointer to the new symbolic region.
+ DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
+ CE, LCtx, CE->getType(), C.blockCount());
+ State = State->BindExpr(CE, LCtx, RetVal);
+
+ // Remember to this region.
+ const auto *SymRegOfRetVal = dyn_cast<SymbolicRegion>(RetVal.getAsRegion());
+ const MemRegion *MR =
+ const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
+ State = State->set<PreviousCallResultMap>(FD, MR);
+
+ ExplodedNode *Node = C.addTransition(State, Note);
+ const NoteTag *PreviousCallNote =
+ C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(MR))
+ return;
+ Out << '\'' << "'previous function call was here" << '\'';
+ });
+
+ C.addTransition(State, Node, PreviousCallNote);
+}
+
+// TODO: This seems really ugly. Simplify this.
+static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
+ const MemRegion *Reg) {
+ while (Reg) {
+ if (State->contains<InvalidMemoryRegions>(Reg))
+ return Reg;
+ const auto *SymBase = Reg->getSymbolicBase();
+ if (!SymBase)
+ break;
+ const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
+ if (!SRV)
+ break;
+ Reg = SRV->getRegion();
+ if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
+ Reg = VarReg;
+ }
+ return nullptr;
+}
+
+// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+// pointer from 'main()' Also, check if invalidated region is passed to a
+// function call as an argument.
+void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check if function invalidates 'envp' argument of 'main'
+ if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // Check if function invalidates the result of previous call
+ if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // Check if one of the arguments of the function call is invalidated
+
+ // If call was inlined, don't report invalidated argument
+ if (C.wasInlined)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
+
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
+ Call.getArgSVal(I).getAsRegion())) {
+ if (const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, SR)) {
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "use of invalidated pointer '";
+ Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
+ C.getASTContext().getPrintingPolicy());
+ Out << "' in a function call";
+
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ Report->addRange(Call.getArgSourceRange(I));
+ C.emitReport(std::move(Report));
+ }
+ }
+ }
+}
+
+// Obtain the environment pointer from 'main()', if present.
+void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+
+ const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
+ if (!FD || FD->param_size() != 3 || !FD->isMain())
+ return;
+
+ ProgramStateRef State = C.getState();
+ const MemRegion *EnvpReg =
+ State->getRegion(FD->parameters()[2], C.getLocationContext());
+
+ // Save the memory region pointed by the environment pointer parameter of
+ // 'main'.
+ State = State->set<EnvPtrRegion>(
+ reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg)));
+ C.addTransition(State);
+}
+
+// Check if invalidated region is being dereferenced.
+void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Ignore memory operations involving 'non-invalidated' locations.
+ const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, Loc.getAsRegion());
+ if (!InvalidatedSymbolicBase)
+ return;
+
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ BT, "dereferencing an invalid pointer", ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ C.emitReport(std::move(Report));
+}
+
+void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<InvalidPtrChecker>();
+}
+
+bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/clang/test/Analysis/cert/env31-c.c b/clang/test/Analysis/cert/env31-c.c
new file mode 100644
index 0000000000000..feaa2baefea7c
--- /dev/null
+++ b/clang/test/Analysis/cert/env31-c.c
@@ -0,0 +1,73 @@
+// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify=putenv,common \
+// RUN: -DENV_INVALIDATING_CALL="putenv(\"X=Y\")"
+//
+// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify=putenvs,common \
+// RUN: -DENV_INVALIDATING_CALL="_putenv_s(\"X\", \"Y\")"
+//
+// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify=wputenvs,common \
+// RUN: -DENV_INVALIDATING_CALL="_wputenv_s(\"X\", \"Y\")"
+//
+// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify=setenv,common \
+// RUN: -DENV_INVALIDATING_CALL="setenv(\"X\", \"Y\", 0)"
+//
+// RUN: %clang_analyze_cc1 -analyzer-output=text -Wno-unused %s \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify=unsetenv,common \
+// RUN: -DENV_INVALIDATING_CALL="unsetenv(\"X\")"
+
+typedef int errno_t;
+typedef char wchar_t;
+
+int putenv(char *string);
+errno_t _putenv_s(const char *varname, const char *value_string);
+errno_t _wputenv_s(const wchar_t *varname, const wchar_t *value_string);
+int setenv(const char *name, const char *value, int overwrite);
+int unsetenv(const char *name);
+
+void fn_without_body(char **e);
+void fn_with_body(char **e) {}
+
+void call_env_invalidating_fn(char **e) {
+ ENV_INVALIDATING_CALL;
+ // putenv-note at -1 5 {{'putenv' call may invalidate the environment parameter of 'main'}}
+ // putenvs-note at -2 5 {{'_putenv_s' call may invalidate the environment parameter of 'main'}}
+ // wputenvs-note at -3 5 {{'_wputenv_s' call may invalidate the environment parameter of 'main'}}
+ // setenv-note at -4 5 {{'setenv' call may invalidate the environment parameter of 'main'}}
+ // unsetenv-note at -5 5 {{'unsetenv' call may invalidate the environment parameter of 'main'}}
+
+ *e;
+ // common-warning at -1 {{dereferencing an invalid pointer}}
+ // common-note at -2 {{dereferencing an invalid pointer}}
+}
+
+int main(int argc, char *argv[], char *envp[]) {
+ char **e = envp;
+ *e; // no-warning
+ e[0]; // no-warning
+ *envp; // no-warning
+ call_env_invalidating_fn(e);
+ // common-note at -1 5 {{Calling 'call_env_invalidating_fn'}}
+ // common-note at -2 4 {{Returning from 'call_env_invalidating_fn'}}
+
+ *e;
+ // common-warning at -1 {{dereferencing an invalid pointer}}
+ // common-note at -2 {{dereferencing an invalid pointer}}
+
+ *envp;
+ // common-warning at -1 2 {{dereferencing an invalid pointer}}
+ // common-note at -2 2 {{dereferencing an invalid pointer}}
+
+ fn_without_body(e);
+ // common-warning at -1 {{use of invalidated pointer 'e' in a function call}}
+ // common-note at -2 {{use of invalidated pointer 'e' in a function call}}
+
+ fn_with_body(e); // no-warning
+}
diff --git a/clang/test/Analysis/cert/env34-c-cert-examples.c b/clang/test/Analysis/cert/env34-c-cert-examples.c
new file mode 100644
index 0000000000000..19ca73f34c7ee
--- /dev/null
+++ b/clang/test/Analysis/cert/env34-c-cert-examples.c
@@ -0,0 +1,101 @@
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=core,alpha.security.cert.env.InvalidPtr \
+// RUN: -verify -Wno-unused %s
+
+#include "../Inputs/system-header-simulator.h"
+char *getenv(const char *name);
+int strcmp(const char*, const char*);
+char *strdup(const char*);
+void free(void *memblock);
+void *malloc(size_t size);
+
+void incorrect_usage(void) {
+ char *tmpvar;
+ char *tempvar;
+
+ tmpvar = getenv("TMP");
+
+ if (!tmpvar)
+ return;
+
+ tempvar = getenv("TEMP");
+
+ if (!tempvar)
+ return;
+
+ if (strcmp(tmpvar, tempvar) == 0) { // body of strcmp is unknown
+ // expected-warning at -1{{use of invalidated pointer 'tmpvar' in a function call}}
+ }
+}
+
+void correct_usage_1(void) {
+ char *tmpvar;
+ char *tempvar;
+
+ const char *temp = getenv("TMP");
+ if (temp != NULL) {
+ tmpvar = (char *)malloc(strlen(temp)+1);
+ if (tmpvar != NULL) {
+ strcpy(tmpvar, temp);
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ temp = getenv("TEMP");
+ if (temp != NULL) {
+ tempvar = (char *)malloc(strlen(temp)+1);
+ if (tempvar != NULL) {
+ strcpy(tempvar, temp);
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (strcmp(tmpvar, tempvar) == 0) {
+ printf("TMP and TEMP are the same.\n");
+ } else {
+ printf("TMP and TEMP are NOT the same.\n");
+ }
+ free(tmpvar);
+ free(tempvar);
+}
+
+void correct_usage_2(void) {
+ char *tmpvar;
+ char *tempvar;
+
+ const char *temp = getenv("TMP");
+ if (temp != NULL) {
+ tmpvar = strdup(temp);
+ if (tmpvar == NULL) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ temp = getenv("TEMP");
+ if (temp != NULL) {
+ tempvar = strdup(temp);
+ if (tempvar == NULL) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (strcmp(tmpvar, tempvar) == 0) {
+ printf("TMP and TEMP are the same.\n");
+ } else {
+ printf("TMP and TEMP are NOT the same.\n");
+ }
+ free(tmpvar);
+ tmpvar = NULL;
+ free(tempvar);
+ tempvar = NULL;
+}
diff --git a/clang/test/Analysis/cert/env34-c.c b/clang/test/Analysis/cert/env34-c.c
new file mode 100644
index 0000000000000..b3b14b0e7c42b
--- /dev/null
+++ b/clang/test/Analysis/cert/env34-c.c
@@ -0,0 +1,331 @@
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=alpha.security.cert.env.InvalidPtr\
+// RUN: -analyzer-output=text -verify -Wno-unused %s
+
+#include "../Inputs/system-header-simulator.h"
+char *getenv(const char *name);
+char *setlocale(int category, const char *locale);
+char *strerror(int errnum);
+
+typedef struct {
+ char * field;
+} lconv;
+lconv *localeconv(void);
+
+typedef struct {
+} tm;
+char *asctime(const tm *timeptr);
+
+int strcmp(const char*, const char*);
+extern void foo(char *e);
+extern char* bar();
+
+
+void getenv_test1() {
+ char *p;
+
+ p = getenv("VAR");
+ *p; // no-warning
+
+ p = getenv("VAR2");
+ *p; // no-warning, getenv result was assigned to the same pointer
+}
+
+void getenv_test2() {
+ char *p, *p2;
+
+ p = getenv("VAR");
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ p2 = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test3() {
+ char *p, *p2, *p3;
+
+ p = getenv("VAR");
+ *p; // no-warning
+
+ p = getenv("VAR2");
+ // expected-note at -1{{previous function call was here}}
+ p2 = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ p3 = getenv("VAR3");
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test4() {
+ char *p, *p2, *p3;
+
+ p = getenv("VAR");
+ // expected-note at -1{{previous function call was here}}
+ p2 = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+ p3 = getenv("VAR3");
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test5() {
+ char *p, *p2, *p3;
+
+ p = getenv("VAR");
+ p2 = getenv("VAR2");
+ // expected-note at -1{{previous function call was here}}
+ p3 = getenv("VAR3");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ *p2;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test6() {
+ char *p, *p2;
+ p = getenv("VAR");
+ *p; // no-warning
+
+ p = getenv("VAR2");
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ p2 = getenv("VAR3");
+ // expected-note at -1{{previous function call was here}}
+ // expected-note at -2{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+
+ *p2; // no-warning
+
+ p = getenv("VAR4");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ *p; // no-warning
+ *p2;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test7() {
+ char *p, *p2;
+ p = getenv("VAR");
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ p2 = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ foo(p);
+ // expected-warning at -1{{use of invalidated pointer 'p' in a function call}}
+ // expected-note at -2{{use of invalidated pointer 'p' in a function call}}
+}
+
+void getenv_test8() {
+ static const char *array[] = {
+ 0,
+ 0,
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ "."
+ };
+
+ if( !array[0] )
+ // expected-note at -1{{Taking true branch}}
+ array[0] = getenv("TEMPDIR");
+ // expected-note at -1{{previous function call was here}}
+
+ if( !array[1] )
+ // expected-note at -1{{Taking true branch}}
+ array[1] = getenv("TMPDIR");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ *array[0];
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test9() {
+ char *p, *p2;
+ p = getenv("something");
+ p = bar();
+ p2 = getenv("something");
+ *p; // no-warning: p does not point to getenv anymore
+}
+
+void getenv_test10() {
+ strcmp(getenv("VAR1"), getenv("VAR2"));
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+ // expected-note at -2{{previous function call was here}}
+ // expected-warning at -3{{use of invalidated pointer 'getenv("VAR1")' in a function call}}
+ // expected-note at -4{{use of invalidated pointer 'getenv("VAR1")' in a function call}}
+}
+
+void dereference_pointer(char* a) {
+ *a;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test11() {
+ char *p = getenv("VAR");
+ // expected-note at -1{{previous function call was here}}
+
+ char *pp = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+ dereference_pointer(p);
+ // expected-note at -1{{Calling 'dereference_pointer'}}
+}
+
+void getenv_test12(int flag1, int flag2) {
+ char *p = getenv("VAR");
+ // expected-note at -1{{previous function call was here}}
+
+ if (flag1) {
+ // expected-note at -1{{Assuming 'flag1' is not equal to 0}}
+ // expected-note at -2{{Taking true branch}}
+ char *pp = getenv("VAR2");
+ // expected-note at -1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+ }
+
+ if (flag2) {
+ // expected-note at -1{{Assuming 'flag2' is not equal to 0}}
+ // expected-note at -2{{Taking true branch}}
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+ }
+}
+
+void setlocale_test1() {
+ char *p, *p2;
+ p = setlocale(0, "VAR");
+ *p; // no-warning
+
+ p = setlocale(0, "VAR2");
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ p2 = setlocale(0, "VAR3");
+ // expected-note at -1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}}
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void setlocale_test2(int flag) {
+ char *p, *p2;
+ p = setlocale(0, "VAR");
+ *p; // no-warning
+
+ p = setlocale(0, "VAR2");
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ if (flag) {
+ // expected-note at -1{{Assuming 'flag' is not equal to 0}}
+ // expected-note at -2{{Taking true branch}}
+ p2 = setlocale(0, "VAR3");
+ // expected-note at -1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}}
+ }
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void strerror_test1() {
+ char *p, *p2;
+
+ p = strerror(0);
+ *p; // no-warning
+
+ p = strerror(1);
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ p2 = strerror(2);
+ // expected-note at -1{{'strerror' call may invalidate the the result of the previous 'strerror'}}
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void strerror_test2(int errno) {
+ char *p, *p2;
+
+ p = strerror(0);
+ *p; // no-warning
+
+ p = strerror(1);
+ // expected-note at -1{{previous function call was here}}
+ *p; // no-warning
+
+ if (0 == 1) {
+ // expected-note at -1{{0 is not equal to 1}}
+ // expected-note at -2{{Taking false branch}}
+ p2 = strerror(2);
+ }
+
+ *p; // no-warning
+
+ if (errno) {
+ // expected-note at -1{{Assuming 'errno' is not equal to 0}}
+ // expected-note at -2{{Taking true branch}}
+ p2 = strerror(errno);
+ // expected-note at -1{{'strerror' call may invalidate the the result of the previous 'strerror'}}
+ }
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void asctime_test() {
+ const tm *t;
+ const tm *tt;
+
+ char* p = asctime(t);
+ // expected-note at -1{{previous function call was here}}
+ char* pp = asctime(tt);
+ // expected-note at -1{{'asctime' call may invalidate the the result of the previous 'asctime'}}
+
+ *p;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void localeconv_test1() {
+ lconv *lc1 = localeconv();
+ // expected-note at -1{{previous function call was here}}
+ lconv *lc2 = localeconv();
+ // expected-note at -1{{'localeconv' call may invalidate the the result of the previous 'localeconv'}}
+
+ *lc1;
+ // expected-warning at -1{{dereferencing an invalid pointer}}
+ // expected-note at -2{{dereferencing an invalid pointer}}
+}
+
+void localeconv_test2() {
+ // TODO: false negative
+ lconv *lc1 = localeconv();
+ lconv *lc2 = localeconv();
+ lc1->field;
+}
More information about the cfe-commits
mailing list