Index: clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def =================================================================== --- clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -328,6 +328,13 @@ "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.", + false) + //===----------------------------------------------------------------------===// // Unsigned analyzer options. //===----------------------------------------------------------------------===// Index: clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -435,7 +435,6 @@ .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 @@ 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. Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -568,6 +568,7 @@ bool DisplayLoadedSummaries = false; bool ModelPOSIX = false; + bool ShouldAssumeControlledEnvironment = false; private: Optional findFunctionSummary(const FunctionDecl *FD, @@ -1433,13 +1434,19 @@ 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 ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { auto *Checker = mgr.registerChecker(); + 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( Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ 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 Index: clang/test/Analysis/assume-controlled-environment.c =================================================================== --- /dev/null +++ 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}} +}