zukatsinadze created this revision.
zukatsinadze added reviewers: NoQ, vsavchenko, Charusso, Szelethus, martong.
zukatsinadze added a project: clang.
Herald added subscribers: steakhal, ASDenysPetrov, ormris, dkrupp, donat.nagy, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun, 
mgorny.
zukatsinadze requested review of this revision.
Herald added a subscriber: cfe-commits.

This patch introduces a new checker: `alpha.security.cert.env.InvalidPtr`

Checker finds usage of invalidated pointers.

Based on the following SEI CERT Rules:
ENV31-C: https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
ENV34-C: https://wiki.sei.cmu.edu/confluence/x/5NUxBQ


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D97699

Files:
  clang/docs/analyzer/checkers.rst
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
  clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
  clang/test/Analysis/cert/env31-c/_putenv_s.c
  clang/test/Analysis/cert/env31-c/_wputenv_s.c
  clang/test/Analysis/cert/env31-c/putenv.c
  clang/test/Analysis/cert/env31-c/setenv.c
  clang/test/Analysis/cert/env31-c/unsetenv.c
  clang/test/Analysis/cert/env34-c-cert-examples.c
  clang/test/Analysis/cert/env34-c.c

Index: clang/test/Analysis/cert/env34-c.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env34-c.c
@@ -0,0 +1,241 @@
+// 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, *p2, *p3;
+
+  p = getenv("VAR");
+  *p; // no-warning
+
+  p = getenv("VAR2");
+  // expected-note@-1{{previous function call was here}}
+  p3 = getenv("VAR2");
+  // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+  p2 = getenv("VAR3");
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test2() {
+  char *p, *p2;
+  p = getenv("VAR");
+  *p; // no-warning
+
+  p = getenv("VAR2");
+  // expected-note@-1{{previous function call was here}}
+  *p; // no-warning
+
+  p2 = getenv("VAR3");
+  // expected-note@-1{{previous function call was here}}
+  // expected-note@-2{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  *p2; // no-warning
+
+  p = getenv("VAR4");
+  // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+  *p; // no-warning
+  *p2;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test3() {
+  char *p, *p2;
+  p = getenv("VAR");
+  // expected-note@-1{{previous function call was here}}
+  *p; // no-warning
+
+  p2 = getenv("VAR2");
+  // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+  foo(p);
+  // expected-warning@-1{{use of invalidated pointer 'p' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'p' in a function call}}
+}
+
+void getenv_test4() {
+  static const char *array[] = {
+     0,
+     0,
+     "/var/tmp",
+     "/usr/tmp",
+     "/tmp",
+     "."
+  };
+
+  if( !array[0] )
+  // expected-note@-1{{Taking true branch}}
+    array[0] = getenv("TEMPDIR");
+    // expected-note@-1{{previous function call was here}}
+
+  if( !array[1] )
+  // expected-note@-1{{Taking true branch}}
+    array[1] = getenv("TMPDIR");
+    // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+
+  *array[0];
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void getenv_test5() {
+  char *p, *p2;
+  p = getenv("something");
+  p = bar();
+  p2 = getenv("something");
+  *p; // no-warning
+}
+
+void getenv_test6() {
+  strcmp(getenv("VAR1"), getenv("VAR2"));
+  // expected-note@-1{{'getenv' call may invalidate the the result of the previous 'getenv'}}
+  // expected-note@-2{{previous function call was here}}
+  // expected-warning@-3{{use of invalidated pointer 'getenv("VAR1")' in a function call}}
+  // expected-note@-4{{use of invalidated pointer 'getenv("VAR1")' in a function call}}
+}
+
+void setlocale_test1() {
+  char *p, *p2;
+  p = setlocale(0, "VAR");
+  *p; // no-warning
+
+  p = setlocale(0, "VAR2");
+  // expected-note@-1{{previous function call was here}}
+  *p; // no-warning
+
+  p2 = setlocale(0, "VAR3");
+  // expected-note@-1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}}
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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@-1{{previous function call was here}}
+  *p; // no-warning
+
+  if (flag) {
+    // expected-note@-1{{Assuming 'flag' is not equal to 0}}
+    // expected-note@-2{{Taking true branch}}
+    p2 = setlocale(0, "VAR3");
+    // expected-note@-1{{'setlocale' call may invalidate the the result of the previous 'setlocale'}}
+  }
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void strerror_test1() {
+  char *p, *p2;
+
+  p = strerror(0);
+  *p; // no-warning
+
+  p = strerror(1);
+  // expected-note@-1{{previous function call was here}}
+  *p; // no-warning
+
+  p2 = strerror(2);
+  // expected-note@-1{{'strerror' call may invalidate the the result of the previous 'strerror'}}
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void strerror_test2(int errno) {
+  char *p, *p2;
+
+  p = strerror(0);
+  *p; // no-warning
+
+  p = strerror(1);
+  // expected-note@-1{{previous function call was here}}
+  *p; // no-warning
+
+  if (0 == 1) {
+    // expected-note@-1{{0 is not equal to 1}}
+    // expected-note@-2{{Taking false branch}}
+    p2 = strerror(2);
+  }
+
+  *p; // no-warning
+
+  if (errno) {
+    // expected-note@-1{{Assuming 'errno' is not equal to 0}}
+    // expected-note@-2{{Taking true branch}}
+    p2 = strerror(errno);
+    // expected-note@-1{{'strerror' call may invalidate the the result of the previous 'strerror'}}
+  }
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void asctime_test() {
+  const tm *t;
+  const tm *tt;
+
+  char* p = asctime(t);
+  // expected-note@-1{{previous function call was here}}
+  char* pp = asctime(tt);
+  // expected-note@-1{{'asctime' call may invalidate the the result of the previous 'asctime'}}
+
+  *p;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void localeconv_test1() {
+  lconv *lc1 = localeconv();
+  // expected-note@-1{{previous function call was here}}
+  lconv *lc2 = localeconv();
+  // expected-note@-1{{'localeconv' call may invalidate the the result of the previous 'localeconv'}}
+
+  *lc1;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+}
+
+void localeconv_test2() {
+  // FIXME: false negative
+  lconv *lc1 = localeconv();
+  lconv *lc2 = localeconv();
+  lc1->field;
+}
Index: clang/test/Analysis/cert/env34-c-cert-examples.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env34-c-cert-examples.c
@@ -0,0 +1,101 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=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@-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;
+}
Index: clang/test/Analysis/cert/env31-c/unsetenv.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env31-c/unsetenv.c
@@ -0,0 +1,34 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=alpha.security.cert.env.InvalidPtr \
+// RUN:  -analyzer-output=text -verify -Wno-unused %s
+
+int unsetenv(const char *name);
+
+void fn_without_body(char** e);
+
+void call_unsetenv(char ** e){
+  unsetenv("name");
+  // expected-note@-1 3 {{'unsetenv' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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_unsetenv(e);
+  //expected-note@-1 3 {{Calling 'call_unsetenv'}}
+  //expected-note@-2 2 {{Returning from 'call_unsetenv'}}
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  fn_without_body(e);
+  // expected-warning@-1{{use of invalidated pointer 'e' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'e' in a function call}}
+}
Index: clang/test/Analysis/cert/env31-c/setenv.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env31-c/setenv.c
@@ -0,0 +1,35 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=alpha.security.cert.env.InvalidPtr\
+// RUN:  -analyzer-output=text -verify -Wno-unused %s
+
+int setenv(const char *name, const char *value, int overwrite);
+
+void fn_without_body(char** e);
+
+void call_setenv(char ** e){
+  setenv("name", "value", 0);
+  // expected-note@-1 3 {{'setenv' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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_setenv(e);
+  //expected-note@-1 3 {{Calling 'call_setenv'}}
+  //expected-note@-2 2 {{Returning from 'call_setenv'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  fn_without_body(e);
+  // expected-warning@-1{{use of invalidated pointer 'e' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'e' in a function call}}
+}
Index: clang/test/Analysis/cert/env31-c/putenv.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env31-c/putenv.c
@@ -0,0 +1,38 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=alpha.security.cert.env.InvalidPtr\
+// RUN:  -analyzer-output=text -verify -Wno-unused %s
+
+int putenv(char *string);
+
+void fn_without_body(char** e);
+
+void call_putenv(char ** e){
+  putenv((char*) "SOMETHING=SOMETHINGELSE");
+  // expected-note@-1 3 {{'putenv' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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_putenv(e);
+  //expected-note@-1 3 {{Calling 'call_putenv'}}
+  //expected-note@-2 2 {{Returning from 'call_putenv'}}
+
+  putenv((char*) "SOMETHING=SOMETHINGELSE");
+  // expected-note@-1 2 {{'putenv' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  fn_with_body(e);
+  // expected-warning@-1{{use of invalidated pointer 'e' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'e' in a function call}}
+}
Index: clang/test/Analysis/cert/env31-c/_wputenv_s.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env31-c/_wputenv_s.c
@@ -0,0 +1,44 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=alpha.security.cert.env.InvalidPtr\
+// RUN:  -analyzer-output=text -verify -Wno-unused %s
+
+typedef char wchar_t;
+typedef int errno_t;
+
+errno_t _wputenv_s(
+   const wchar_t *varname,
+   const wchar_t *value_string
+);
+
+void fn_without_body(char** e);
+void fn_with_body(char** e) {}
+
+void call_putenv(char ** e){
+  _wputenv_s((char*) "SOMETHING", (char*) "SOMETHINGELSE");
+  // expected-note@-1 3 {{'_wputenv_s' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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_putenv(e);
+  //expected-note@-1 3 {{Calling 'call_putenv'}}
+  //expected-note@-2 2 {{Returning from 'call_putenv'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  fn_without_body(e);
+  // expected-warning@-1{{use of invalidated pointer 'e' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'e' in a function call}}
+
+  fn_with_body(e); // no-warning
+}
Index: clang/test/Analysis/cert/env31-c/_putenv_s.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/cert/env31-c/_putenv_s.c
@@ -0,0 +1,43 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=alpha.security.cert.env.InvalidPtr\
+// RUN:  -analyzer-output=text -verify -Wno-unused %s
+
+typedef int errno_t;
+
+errno_t _putenv_s(
+   const char *varname,
+   const char *value_string
+);
+
+void fn_without_body(char** e);
+void fn_with_body(char** e) {}
+
+void call_putenv(char ** e){
+  _putenv_s((char*) "SOMETHING", (char*) "SOMETHINGELSE");
+  // expected-note@-1 3 {{'_putenv_s' call may invalidate the environment parameter of 'main'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-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_putenv(e);
+  //expected-note@-1 3 {{Calling 'call_putenv'}}
+  //expected-note@-2 2 {{Returning from 'call_putenv'}}
+
+  *e;
+  // expected-warning@-1{{dereferencing an invalid pointer}}
+  // expected-note@-2{{dereferencing an invalid pointer}}
+
+  fn_without_body(e);
+  // expected-warning@-1{{use of invalidated pointer 'e' in a function call}}
+  // expected-note@-2{{use of invalidated pointer 'e' in a function call}}
+
+  fn_with_body(e); // no-warning
+}
Index: clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
@@ -0,0 +1,382 @@
+//== 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/AST/ASTContext.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#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"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <memory>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class InvalidPtrChecker
+    : public Checker<check::Location, check::BeginFunction, eval::Call,
+                     check::PostCall, check::DeadSymbols> {
+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},
+  };
+
+  using EvalFn = bool (InvalidPtrChecker::*)(const CallEvent &Call,
+                                             CheckerContext &C) const;
+
+  bool evalPreviousReturnInvalidatingCall(StringRef FunctionName,
+                                          const CallEvent &Call,
+                                          CheckerContext &C) const;
+
+  bool evalGetenv(const CallEvent &Call, CheckerContext &C) const {
+    return evalPreviousReturnInvalidatingCall("getenv", Call, C);
+  }
+
+  bool evalSetlocale(const CallEvent &Call, CheckerContext &C) const {
+    return evalPreviousReturnInvalidatingCall("setlocale", Call, C);
+  }
+
+  bool evalStrerror(const CallEvent &Call, CheckerContext &C) const {
+    return evalPreviousReturnInvalidatingCall("strerror", Call, C);
+  }
+
+  bool evalLocaleconv(const CallEvent &Call, CheckerContext &C) const {
+    return evalPreviousReturnInvalidatingCall("localeconv", Call, C);
+  }
+
+  bool evalAsctime(const CallEvent &Call, CheckerContext &C) const {
+    return evalPreviousReturnInvalidatingCall("asctime", Call, C);
+  }
+
+  // SEI CERT ENV34-C
+  const CallDescriptionMap<EvalFn> PreviousCallInvalidatingFunctions = {
+      {{"getenv", 1}, &InvalidPtrChecker::evalGetenv},
+      {{"setlocale", 2}, &InvalidPtrChecker::evalSetlocale},
+      {{"strerror", 1}, &InvalidPtrChecker::evalStrerror},
+      {{"localeconv", 0}, &InvalidPtrChecker::evalLocaleconv},
+      {{"asctime", 1}, &InvalidPtrChecker::evalAsctime},
+  };
+
+public:
+  // Obtain the environment pointer from 'main()' (if present).
+  void checkBeginFunction(CheckerContext &C) const;
+
+  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+  // pointer from 'main()' Also, check if invalidated region is passed to a
+  // conservatively evaluated function call as an argument.
+  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+  // Handle functions in PreviousCallInvalidatingFunctions.
+  bool evalCall(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;
+
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+};
+
+class PreviousCallVisitor : public BugReporterVisitor {
+public:
+  PreviousCallVisitor(const MemRegion *InvalidatedSymbolicBase)
+      : InvalidatedSymbolicBase(InvalidatedSymbolicBase) {}
+  void Profile(llvm::FoldingSetNodeID &ID) const override {
+    static int X = 0;
+    ID.AddPointer(&X);
+  }
+  PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
+                                   BugReporterContext &BRC,
+                                   PathSensitiveBugReport &BR) override;
+
+private:
+  bool Satisfied = false;
+  const MemRegion *InvalidatedSymbolicBase;
+};
+
+} // 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 *'
+REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
+
+// Stores key-value pairs, where key is function name and value is pointer to
+// memory region returned by previous call of this function
+REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const char *,
+                               const MemRegion *)
+
+bool InvalidPtrChecker::evalCall(const CallEvent &Call,
+                                 CheckerContext &C) const {
+  if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
+    return (this->**Handler)(Call, C);
+  return false;
+}
+
+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);
+}
+
+bool InvalidPtrChecker::evalPreviousReturnInvalidatingCall(
+    StringRef FunctionName, const CallEvent &Call, CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+
+  const NoteTag *Note = nullptr;
+
+  // Invalidate the region of the previously returned pointer - if there was
+  // one.
+  if (const auto *Reg =
+          State->get<PreviousCallResultMap>(FunctionName.data())) {
+    const auto *PrevReg = *Reg;
+    State = State->add<InvalidMemoryRegions>(PrevReg);
+    Note = C.getNoteTag([PrevReg, FunctionName](PathSensitiveBugReport &BR,
+                                                llvm::raw_ostream &Out) {
+      if (!BR.isInteresting(PrevReg))
+        return;
+      Out << '\'' << FunctionName
+          << "' call may invalidate the the result of the previous " << '\''
+          << FunctionName << '\'';
+    });
+  }
+
+  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());
+  State = State->set<PreviousCallResultMap>(
+      FunctionName.data(),
+      const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion()));
+
+  C.addTransition(State, Note);
+  return true;
+}
+
+// FIXME: 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 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));
+        Report->addVisitor(
+            std::make_unique<PreviousCallVisitor>(InvalidatedSymbolicBase));
+
+        C.emitReport(std::move(Report));
+        C.addTransition(State);
+      }
+    }
+  }
+}
+
+// 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);
+  Report->addVisitor(
+      std::make_unique<PreviousCallVisitor>(InvalidatedSymbolicBase));
+  C.emitReport(std::move(Report));
+}
+
+void InvalidPtrChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+                                         CheckerContext &C) const {
+  // TODO: some information is garbage collected too early.
+
+  /*ProgramStateRef State = C.getState();
+  InvalidMemoryRegionsTy TrackedRegions = State->get<InvalidMemoryRegions>();
+  for (auto E : TrackedRegions) {
+    if (!SymReaper.isLiveRegion(E)) {
+      State = State->remove<InvalidMemoryRegions>(E);
+    }
+  }
+  C.addTransition(State);*/
+}
+
+PathDiagnosticPieceRef
+PreviousCallVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+                               PathSensitiveBugReport &BR) {
+  // Stop traversal if previous call was found already
+  if (Satisfied)
+    return nullptr;
+
+  const Stmt *S = N->getStmtForDiagnostics();
+  if (!S)
+    return nullptr;
+
+  const auto *CallE = dyn_cast<CallExpr>(S);
+  if (!CallE)
+    return nullptr;
+
+  // Region associated with the current call expression.
+  const MemRegion *M = N->getSVal(CallE).getAsRegion();
+  if (!M)
+    return nullptr;
+
+  // Check if target region is one we want.
+  if (M != InvalidatedSymbolicBase)
+    return nullptr;
+
+  // Stop traversal on this path.
+  Satisfied = true;
+
+  SmallString<256> Buf;
+  llvm::raw_svector_ostream Out(Buf);
+  Out << "previous function call was here";
+  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+                             N->getLocationContext());
+
+  return std::make_shared<PathDiagnosticEventPiece>(Pos, Out.str(), true);
+}
+
+void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<InvalidPtrChecker>();
+}
+
+bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
+  return true;
+}
Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -39,6 +39,7 @@
   DivZeroChecker.cpp
   DynamicTypePropagation.cpp
   DynamicTypeChecker.cpp
+  cert/InvalidPtrChecker.cpp
   EnumCastOutOfRangeChecker.cpp
   ExprInspectionChecker.cpp
   FixedAddressChecker.cpp
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -73,6 +73,7 @@
 
 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>;
@@ -932,11 +933,19 @@
 
   def PutenvWithAuto : Checker<"34c">,
   HelpText<"Finds calls to the 'putenv' function which pass a pointer to "
-           "an automatic variable as the argument.">,
+           "an automatic variable as the argument">,
   Documentation<HasDocumentation>;
 
 } // 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">,
Index: clang/docs/analyzer/checkers.rst
===================================================================
--- clang/docs/analyzer/checkers.rst
+++ clang/docs/analyzer/checkers.rst
@@ -2053,6 +2053,7 @@
 
 SEI CERT checkers which tries to find errors based on their `C coding rules <https://wiki.sei.cmu.edu/confluence/display/c/2+Rules>`_.
 
+
 .. _alpha-security-cert-pos-checkers:
 
 alpha.security.cert.pos
@@ -2074,10 +2075,38 @@
     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-31c:
+
+alpha.security.cert.env.31c
+"""""""""""""""""""""""""""
+Finds usage of possibly invalidated environment pointer.
+
+.. 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.
+      }
+    }
+    return 0;
+  }
+
 .. _alpha-security-ArrayBound:
 
 alpha.security.ArrayBound (C)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to