[clang] edde4ef - [analyzer] Introduce the assume-controlled-environment config option

Balazs Benics via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 13 01:51:20 PDT 2021


Author: Balazs Benics
Date: 2021-10-13T10:50:26+02:00
New Revision: edde4efc66df2257f0b2351d5f98b4fbb2ced620

URL: https://github.com/llvm/llvm-project/commit/edde4efc66df2257f0b2351d5f98b4fbb2ced620
DIFF: https://github.com/llvm/llvm-project/commit/edde4efc66df2257f0b2351d5f98b4fbb2ced620.diff

LOG: [analyzer] Introduce the assume-controlled-environment config option

If the `assume-controlled-environment` is `true`, we should expect `getenv()`
to succeed, and the result should not be considered tainted.
By default, the option will be `false`.

Reviewed By: NoQ, martong

Differential Revision: https://reviews.llvm.org/D111296

Added: 
    clang/test/Analysis/assume-controlled-environment.c

Modified: 
    clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
    clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
    clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
    clang/test/Analysis/analyzer-config.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
index 16ca7a8456e39..aab8e1284bf64 100644
--- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
+++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def
@@ -328,6 +328,14 @@ ANALYZER_OPTION(
     "holds a single element.",
     false)
 
+ANALYZER_OPTION(
+    bool, ShouldAssumeControlledEnvironment, "assume-controlled-environment",
+    "Whether the analyzed application runs in a controlled environment. "
+    "We will assume that environment variables exist in queries and they hold "
+    "no malicious data. For instance, if this option is enabled, 'getenv()' "
+    "might be modeled by the analyzer to never return NULL.",
+    false)
+
 //===----------------------------------------------------------------------===//
 // Unsigned analyzer options.
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 42c777eb2c521..3fc046015f151 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -435,7 +435,6 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
           .Case("getch", {{}, {ReturnValueIndex}})
           .Case("getchar", {{}, {ReturnValueIndex}})
           .Case("getchar_unlocked", {{}, {ReturnValueIndex}})
-          .Case("getenv", {{}, {ReturnValueIndex}})
           .Case("gets", {{}, {0, ReturnValueIndex}})
           .Case("scanf", {{}, {}, VariadicType::Dst, 1})
           .Case("socket", {{},
@@ -468,6 +467,16 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
 
   if (!Rule.isNull())
     return Rule;
+
+  // `getenv` returns taint only in untrusted environments.
+  if (FData.FullName == "getenv") {
+    if (C.getAnalysisManager()
+            .getAnalyzerOptions()
+            .ShouldAssumeControlledEnvironment)
+      return {};
+    return {{}, {ReturnValueIndex}};
+  }
+
   assert(FData.FDecl);
 
   // Check if it's one of the memory setting/copying functions.

diff  --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 74adc5882bfbf..e8b963a535d8b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -568,6 +568,7 @@ class StdLibraryFunctionsChecker
 
   bool DisplayLoadedSummaries = false;
   bool ModelPOSIX = false;
+  bool ShouldAssumeControlledEnvironment = false;
 
 private:
   Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
@@ -1433,13 +1434,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
                 RetType{Ssize_tTy}),
       GetLineSummary);
 
-  // char *getenv(const char *name);
-  addToFunctionSummaryMap(
-      "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}),
-      Summary(NoEvalCall)
-          .Case({NotNull(Ret)})
-          .Case({NotNull(Ret)->negate()})
-          .ArgConstraint(NotNull(ArgNo(0))));
+  {
+    Summary GetenvSummary = Summary(NoEvalCall)
+                                .ArgConstraint(NotNull(ArgNo(0)))
+                                .Case({NotNull(Ret)});
+    // In untrusted environments the envvar might not exist.
+    if (!ShouldAssumeControlledEnvironment)
+      GetenvSummary.Case({NotNull(Ret)->negate()});
+
+    // char *getenv(const char *name);
+    addToFunctionSummaryMap(
+        "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}),
+        std::move(GetenvSummary));
+  }
 
   if (ModelPOSIX) {
 
@@ -2653,11 +2660,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
 
 void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
   auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>();
+  const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
   Checker->DisplayLoadedSummaries =
-      mgr.getAnalyzerOptions().getCheckerBooleanOption(
-          Checker, "DisplayLoadedSummaries");
-  Checker->ModelPOSIX =
-      mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
+      Opts.getCheckerBooleanOption(Checker, "DisplayLoadedSummaries");
+  Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX");
+  Checker->ShouldAssumeControlledEnvironment =
+      Opts.ShouldAssumeControlledEnvironment;
 }
 
 bool ento::shouldRegisterStdCLibraryFunctionsChecker(

diff  --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c
index 4cca1cb553b62..429229f6b6ca4 100644
--- a/clang/test/Analysis/analyzer-config.c
+++ b/clang/test/Analysis/analyzer-config.c
@@ -15,6 +15,7 @@
 // CHECK-NEXT: apiModeling.StdCLibraryFunctions:DisplayLoadedSummaries = false
 // CHECK-NEXT: apiModeling.StdCLibraryFunctions:ModelPOSIX = false
 // CHECK-NEXT: apply-fixits = false
+// CHECK-NEXT: assume-controlled-environment = false
 // CHECK-NEXT: avoid-suppressing-null-argument-paths = false
 // CHECK-NEXT: c++-allocator-inlining = true
 // CHECK-NEXT: c++-container-inlining = false

diff  --git a/clang/test/Analysis/assume-controlled-environment.c b/clang/test/Analysis/assume-controlled-environment.c
new file mode 100644
index 0000000000000..749b8198f6fb0
--- /dev/null
+++ b/clang/test/Analysis/assume-controlled-environment.c
@@ -0,0 +1,22 @@
+// RUN: %clang_analyze_cc1 -verify=untrusted-env %s \
+// RUN:   -analyzer-checker=core                    \
+// RUN:   -analyzer-checker=alpha.security.taint    \
+// RUN:   -analyzer-checker=debug.TaintTest
+
+// RUN: %clang_analyze_cc1 -verify %s -DEXPECT_NO_WARNINGS    \
+// RUN:   -analyzer-config assume-controlled-environment=true \
+// RUN:   -analyzer-checker=core                              \
+// RUN:   -analyzer-checker=alpha.security.taint              \
+// RUN:   -analyzer-checker=debug.TaintTest
+
+
+#ifdef EXPECT_NO_WARNINGS
+// expected-no-diagnostics
+#endif
+
+char *getenv(const char *name);
+
+void foo() {
+  char *p = getenv("FOO"); // untrusted-env-warning {{tainted}}
+  (void)p;                 // untrusted-env-warning {{tainted}}
+}


        


More information about the cfe-commits mailing list