diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -973,26 +973,16 @@ const MemRegion *sReg = nullptr; if (D->hasGlobalStorage() && !D->isStaticLocal()) { - - // First handle the globals defined in system headers. - if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) { - // Allow the system globals which often DO GET modified, assume the - // rest are immutable. - if (D->getName().contains("errno")) - sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); - else - sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); - - // Treat other globals as GlobalInternal unless they are constants. - } else { - QualType GQT = D->getType(); - const Type *GT = GQT.getTypePtrOrNull(); + QualType Ty = D->getType(); + assert(!Ty.isNull()); + if (Ty.isConstQualified() && Ty->isArithmeticType()) { // TODO: We could walk the complex types here and see if everything is // constified. - if (GT && GQT.isConstQualified() && GT->isArithmeticType()) - sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); - else - sReg = getGlobalsRegion(); + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + } else if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) { + sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + } else { + sReg = getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind); } // Finally handle static locals. diff --git a/clang/test/Analysis/Inputs/some_system_globals.h b/clang/test/Analysis/Inputs/some_system_globals.h new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/some_system_globals.h @@ -0,0 +1,4 @@ +#pragma clang system_header + +extern const int my_const_system_global; +extern int my_mutable_system_global; diff --git a/clang/test/Analysis/Inputs/some_user_globals.h b/clang/test/Analysis/Inputs/some_user_globals.h new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/Inputs/some_user_globals.h @@ -0,0 +1,2 @@ +extern const int my_const_user_global; +extern int my_mutable_user_global; diff --git a/clang/test/Analysis/global-region-invalidation.c b/clang/test/Analysis/global-region-invalidation.c --- a/clang/test/Analysis/global-region-invalidation.c +++ b/clang/test/Analysis/global-region-invalidation.c @@ -28,11 +28,17 @@ void foo(void); int stdinTest(void) { int i = 0; - fscanf(stdin, "%d", &i); + fscanf(stdin, "%d", &i); // (1) foo(); int m = i; // expected-warning + {{tainted}} - fscanf(stdin, "%d", &i); - int j = i; // expected-warning + {{tainted}} + fscanf(stdin, "%d", &i); // (2) + + // The 'stdin' mutable system global variable gets invalidated by the first + // (1) opaque 'fscanf()' call. Consequently, the subsequent + // (2) 'fscanf(stdin, ...)' call won't propagate taint. + // FIXME: The analyzer engine should propagate taint by default on + // invalidation. + int j = i; // FIXME: should have 'tainted' warning here. return m + j; // expected-warning + {{tainted}} } diff --git a/clang/test/Analysis/globals-are-not-always-immutable.c b/clang/test/Analysis/globals-are-not-always-immutable.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/globals-are-not-always-immutable.c @@ -0,0 +1,56 @@ +// RUN: %clang_analyze_cc1 -verify %s \ +// RUN: -analyzer-config eagerly-assume=false \ +// RUN: -analyzer-checker=core,debug.ExprInspection + +#include "Inputs/errno_var.h" +#include "Inputs/some_system_globals.h" +#include "Inputs/some_user_globals.h" + +void invalidate_globals(void); +void clang_analyzer_eval(int x); + +/// Test the special system 'errno' +void test_errno() { + errno = 2; + clang_analyzer_eval(errno == 2); // expected-warning {{TRUE}} + invalidate_globals(); + clang_analyzer_eval(errno == 2); // expected-warning {{UNKNOWN}} +} + +/// Test user global variables +void test_my_const_user_global() { + if (my_const_user_global != 2) + return; + + clang_analyzer_eval(my_const_user_global == 2); // expected-warning {{TRUE}} + invalidate_globals(); + clang_analyzer_eval(my_const_user_global == 2); // expected-warning {{TRUE}} +} + +void test_my_mutable_user_global() { + if (my_mutable_user_global != 2) + return; + + clang_analyzer_eval(my_mutable_user_global == 2); // expected-warning {{TRUE}} + invalidate_globals(); + clang_analyzer_eval(my_mutable_user_global == 2); // expected-warning {{UNKNOWN}} +} + +/// Test system global variables +void test_my_const_system_global() { + if (my_const_system_global != 2) + return; + + clang_analyzer_eval(my_const_system_global == 2); // expected-warning {{TRUE}} + invalidate_globals(); + clang_analyzer_eval(my_const_system_global == 2); // expected-warning {{TRUE}} +} + +void test_my_mutable_system_global() { + if (my_mutable_system_global != 2) + return; + + clang_analyzer_eval(my_mutable_system_global == 2); // expected-warning {{TRUE}} + invalidate_globals(); + clang_analyzer_eval(my_mutable_system_global == 2); // expected-warning {{UNKNOWN}} It was previously TRUE. +}