diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -66,7 +66,20 @@ core.NullDereference (C, C++, ObjC) """"""""""""""""""""""""""""""""""" -Check for dereferences of null pointers. +Check for dereferences of null pointers. + +This checker specifically does +not report null pointer dereferences for x86 and x86-64 targets when the +address space is 256 (x86 GS Segment), 257 (x86 FS Segment), or 258 (x86 SS +segment). See `X86/X86-64 Language Extensions +`__ +for reference. + +The ``SuppressAddressSpaces`` option suppresses +warnings for null dereferences of all pointers with address spaces. You can +disable this behavior with the option +``-analyzer-config core.NullDereference:SuppressAddressSpaces=false``. +*Defaults to true*. .. code-block:: objc diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -191,6 +191,13 @@ def DereferenceChecker : Checker<"NullDereference">, HelpText<"Check for dereferences of null pointers">, + CheckerOptions<[ + CmdLineOption + ]>, Documentation; def NonNullParamChecker : Checker<"NonNullParamChecker">, diff --git a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -11,9 +11,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprOpenMP.h" +#include "clang/Basic/TargetInfo.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" @@ -39,6 +40,8 @@ void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + bool suppressReport(CheckerContext &C, const Expr *E) const; + public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; @@ -49,6 +52,8 @@ const Expr *Ex, const ProgramState *state, const LocationContext *LCtx, bool loadedFrom = false); + + DefaultBool SuppressAddressSpaces; }; } // end anonymous namespace @@ -109,9 +114,35 @@ return E; } -static bool suppressReport(const Expr *E) { - // Do not report dereferences on memory in non-default address spaces. - return E->getType().hasAddressSpace(); +bool DereferenceChecker::suppressReport(CheckerContext &C, + const Expr *E) const { + // Do not report dereferences on memory that use address space #256, #257, + // and #258. Those address spaces are used when dereferencing address spaces + // relative to the GS, FS, and SS segments on x86/x86-64 targets. + // Dereferencing a null pointer in these address spaces is not defined + // as an error. All other null dereferences in other address spaces + // are defined as an error unless explicitly defined. + // See https://clang.llvm.org/docs/LanguageExtensions.html, the section + // "X86/X86-64 Language Extensions" + + QualType Ty = E->getType(); + if (!Ty.hasAddressSpace()) + return false; + if (SuppressAddressSpaces) + return true; + + const llvm::Triple::ArchType Arch = + C.getASTContext().getTargetInfo().getTriple().getArch(); + + if ((Arch == llvm::Triple::x86) || (Arch == llvm::Triple::x86_64)) { + switch (toTargetAddressSpace(E->getType().getAddressSpace())) { + case 256: + case 257: + case 258: + return true; + } + } + return false; } static bool isDeclRefExprToReference(const Expr *E) { @@ -209,7 +240,7 @@ // Check for dereference of an undefined value. if (l.isUndef()) { const Expr *DerefExpr = getDereferenceExpr(S); - if (!suppressReport(DerefExpr)) + if (!suppressReport(C, DerefExpr)) reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C); return; } @@ -230,7 +261,7 @@ // We know that 'location' can only be null. This is what // we call an "explicit" null dereference. const Expr *expr = getDereferenceExpr(S); - if (!suppressReport(expr)) { + if (!suppressReport(C, expr)) { reportBug(DerefKind::NullPointer, nullState, expr, C); return; } @@ -272,7 +303,7 @@ if (StNull) { if (!StNonNull) { const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); - if (!suppressReport(expr)) { + if (!suppressReport(C, expr)) { reportBug(DerefKind::NullPointer, StNull, expr, C); return; } @@ -308,7 +339,9 @@ } void ento::registerDereferenceChecker(CheckerManager &mgr) { - mgr.registerChecker(); + auto *Chk = mgr.registerChecker(); + Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption( + mgr.getCurrentCheckerName(), "SuppressAddressSpaces"); } bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) { diff --git a/clang/test/Analysis/analyzer-config.c b/clang/test/Analysis/analyzer-config.c --- a/clang/test/Analysis/analyzer-config.c +++ b/clang/test/Analysis/analyzer-config.c @@ -41,6 +41,7 @@ // CHECK-NEXT: core.CallAndMessage:NilReceiver = true // CHECK-NEXT: core.CallAndMessage:ParameterCount = true // CHECK-NEXT: core.CallAndMessage:UndefReceiver = true +// CHECK-NEXT: core.NullDereference:SuppressAddressSpaces = true // CHECK-NEXT: cplusplus.Move:WarnOn = KnownsAndLocals // CHECK-NEXT: cplusplus.SmartPtrModeling:ModelSmartPtrDereference = false // CHECK-NEXT: crosscheck-with-z3 = false diff --git a/clang/test/Analysis/cast-value-notes.cpp b/clang/test/Analysis/cast-value-notes.cpp --- a/clang/test/Analysis/cast-value-notes.cpp +++ b/clang/test/Analysis/cast-value-notes.cpp @@ -4,8 +4,21 @@ // // RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \ // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -DSUPPRESS_ADDRESS_SPACES\ // RUN: -analyzer-output=text -verify -DAMDGCN_TRIPLE %s 2>&1 | FileCheck %s -check-prefix=AMDGCN-CHECK +// RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=false -analyzer-output=text\ +// RUN: -verify -DAMDGCN_TRIPLE %s 2>&1 | FileCheck %s\ +// RUN: -check-prefix=AMDGCN-CHECK-WARNADDRSPACE + +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-unknown-unknown\ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=false -analyzer-output=text\ +// RUN: -verify -DDEFAULT_TRIPLE %s 2>&1 | FileCheck %s\ +// RUN: -check-prefix=DEFAULT-CHECK + #include "Inputs/llvm.h" // The amggcn triple case uses an intentionally different address space. @@ -48,6 +61,7 @@ (void)C; } #elif defined(AMDGCN_TRIPLE) +#if defined(SUPPRESS_ADDRESS_SPACES) void evalReferences(const Shape &S) { const auto &C = dyn_cast(S); clang_analyzer_printState(); @@ -56,6 +70,18 @@ (void)C; } #else +void evalReferences(const Shape &S) { + const auto &C = dyn_cast(S); + // expected-note@-1 {{Assuming 'S' is not a 'Circle'}} + // expected-note@-2 {{Dereference of null pointer}} + // expected-warning@-3 {{Dereference of null pointer}} + clang_analyzer_printState(); + // AMDGCN-CHECK-WARNADDRSPACE: "dynamic_types": [ + // AMDGCN-CHECK-WARNADDRSPACE-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true } + (void)C; +} +#endif +#else #error Target must be specified, and must be pinned #endif