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,17 @@ 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 ``DetectAllNullDereferences`` option enables the checker to emit +warnings for null dereferences of all pointers. You can enable with the +``-analyzer-config core.NullDereference:DetectAllNullDereferences=true``. +*Defaults to false*. .. 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 @@ -39,6 +39,8 @@ void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + bool suppressReport(const Expr *E) const; + public: void checkLocation(SVal location, bool isLoad, const Stmt* S, CheckerContext &C) const; @@ -49,6 +51,8 @@ const Expr *Ex, const ProgramState *state, const LocationContext *LCtx, bool loadedFrom = false); + + bool DetectAllNullDereferences = false; }; } // end anonymous namespace @@ -109,9 +113,24 @@ 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(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" + if (isTargetAddressSpace(E->getType().getAddressSpace())) { + switch (toTargetAddressSpace(E->getType().getAddressSpace())) { + case 256: + case 257: + case 258: + return true; + } + } + return (E->getType().hasAddressSpace() && !DetectAllNullDereferences); } static bool isDeclRefExprToReference(const Expr *E) { @@ -308,7 +327,11 @@ } void ento::registerDereferenceChecker(CheckerManager &mgr) { - mgr.registerChecker(); + auto *Chk = mgr.registerChecker(); + // auto *Chk = mgr.getChecker(); + Chk->DetectAllNullDereferences = + mgr.getAnalyzerOptions().getCheckerBooleanOption( + mgr.getCurrentCheckerName(), "DetectAllNullDereferences"); } 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:DetectAllNullDereferences = false // 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 @@ -6,6 +6,12 @@ // RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ // 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:DetectAllNullDereferences=true -analyzer-output=text\ +// RUN: -verify -DDETECT_ALL_NULL_DEREFERENCES -DAMDGCN_TRIPLE %s 2>&1 | FileCheck %s\ +// RUN: -check-prefix=AMDGCN-CHECK-WARNADDRSPACE + #include "Inputs/llvm.h" // The amggcn triple case uses an intentionally different address space. @@ -48,6 +54,18 @@ (void)C; } #elif defined(AMDGCN_TRIPLE) +#if defined(DETECT_ALL_NULL_DEREFERENCES) +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; +} +#else void evalReferences(const Shape &S) { const auto &C = dyn_cast(S); clang_analyzer_printState(); @@ -55,6 +73,7 @@ // AMDGCN-CHECK-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