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); + + bool SuppressAddressSpaces = false; }; } // 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 @@ -1,10 +1,44 @@ -// RUN: %clang_analyze_cc1 -std=c++14 \ -// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ -// RUN: -analyzer-output=text -verify -DDEFAULT_TRIPLE %s 2>&1 | FileCheck %s -check-prefix=DEFAULT-CHECK +// RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-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\ +// RUN: -analyzer-output=text -verify -DX86 -DNOT_SUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-CHECK // // RUN: %clang_analyze_cc1 -std=c++14 -triple amdgcn-unknown-unknown \ // 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: -analyzer-config core.NullDereference:SuppressAddressSpaces=true\ +// RUN: -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s -check-prefix=X86-CHECK +// +// RUN: %clang_analyze_cc1 -std=c++14 -triple x86_64-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK +// +// 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=true\ +// RUN: -analyzer-output=text -verify -DX86 -DSUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK-SUPPRESSED +// +// 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\ +// RUN: -analyzer-output=text -verify -DX86 -DNOT_SUPPRESSED %s 2>&1 | FileCheck %s --check-prefix=X86-CHECK +// +// RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-output=text -verify -DMIPS %s 2>&1 +// +// RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=false\ +// RUN: -analyzer-output=text -verify -DMIPS %s 2>&1 +// +// RUN: %clang_analyze_cc1 -std=c++14 -triple mips-unknown-unknown \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ +// RUN: -analyzer-config core.NullDereference:SuppressAddressSpaces=true\ +// RUN: -analyzer-output=text -verify -DMIPS_SUPPRESSED %s #include "Inputs/llvm.h" @@ -36,27 +70,51 @@ void clang_analyzer_printState(); -#if defined(DEFAULT_TRIPLE) +#if defined(X86) 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(); - // DEFAULT-CHECK: "dynamic_types": [ - // DEFAULT-CHECK-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const class clang::Circle &", "sub_classable": true } + // XX86-CHECK: "dynamic_types": [ + // XX86-CHECK-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const class clang::Circle &", "sub_classable": true } (void)C; } -#elif defined(AMDGCN_TRIPLE) -void evalReferences(const Shape &S) { +#if defined(SUPPRESSED) +void evalReferences_addrspace(const Shape &S) { const auto &C = dyn_cast(S); clang_analyzer_printState(); - // AMDGCN-CHECK: "dynamic_types": [ - // AMDGCN-CHECK-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true } + // X86-CHECK-SUPPRESSED: "dynamic_types": [ + // X86-CHECK-SUPPRESSED-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true } (void)C; } -#else -#error Target must be specified, and must be pinned +#endif +#if defined(NOT_SUPPRESSED) +void evalReferences_addrspace(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(); + // X86-CHECK: "dynamic_types": [ + // X86-CHECK-NEXT: { "region": "SymRegion{reg_$0}", "dyn_type": "const __attribute__((address_space(3))) class clang::Circle &", "sub_classable": true } + (void)C; +} +#endif +#elif defined(MIPS) +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}} +} +#if defined(MIPS_SUPPRESSED) +void evalReferences_addrspace(const Shape &S) { + const auto &C = dyn_cast(S); + (void)C; +} +#endif #endif void evalNonNullParamNonNullReturnReference(const Shape &S) {