[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