[analyzer] Add option for AddrSpace in core.NullDereference check

This change adds an option to detect all null dereferences for
    non-default address spaces, except for address spaces 256, 257 and 258.
    Those address spaces are special since null dereferences are not errors.

    All address spaces can be considered (except for 256, 257, and 258) by
    using -analyzer-config
    core.NullDereference:DetectAllNullDereferences=true. This option is
    false by default, retaining the original behavior.

    A LIT test was enhanced to cover this case, and the rst documentation
    was updated to describe this behavior.

Reviewed By: steakhal

Differential Revision: https://reviews.llvm.org/D122841
This commit is contained in:
Vince Bridgers 2022-04-08 06:17:14 -05:00 committed by einvbri
parent 7a21a0525a
commit 3566bbe62f
5 changed files with 134 additions and 22 deletions

View File

@ -66,7 +66,20 @@ Check for null pointers passed as arguments to a function whose arguments are re
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
<https://clang.llvm.org/docs/LanguageExtensions.html#memory-references-to-specified-segments>`__
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

View File

@ -191,6 +191,13 @@ def CallAndMessageChecker : Checker<"CallAndMessage">,
def DereferenceChecker : Checker<"NullDereference">,
HelpText<"Check for dereferences of null pointers">,
CheckerOptions<[
CmdLineOption<Boolean,
"SuppressAddressSpaces",
"Suppresses warning when pointer dereferences an address space",
"true",
Released>
]>,
Documentation<HasDocumentation>;
def NonNullParamChecker : Checker<"NonNullParamChecker">,

View File

@ -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 @@ class DereferenceChecker
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 @@ public:
const Expr *Ex, const ProgramState *state,
const LocationContext *LCtx,
bool loadedFrom = false);
bool SuppressAddressSpaces = false;
};
} // end anonymous namespace
@ -109,9 +114,35 @@ static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
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 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// 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 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
// 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 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
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 DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
}
void ento::registerDereferenceChecker(CheckerManager &mgr) {
mgr.registerChecker<DereferenceChecker>();
auto *Chk = mgr.registerChecker<DereferenceChecker>();
Chk->SuppressAddressSpaces = mgr.getAnalyzerOptions().getCheckerBooleanOption(
mgr.getCurrentCheckerName(), "SuppressAddressSpaces");
}
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {

View File

@ -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

View File

@ -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-output=text -verify -DAMDGCN_TRIPLE %s 2>&1 | FileCheck %s -check-prefix=AMDGCN-CHECK
// 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-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 @@ using namespace clang;
void clang_analyzer_printState();
#if defined(DEFAULT_TRIPLE)
#if defined(X86)
void evalReferences(const Shape &S) {
const auto &C = dyn_cast<Circle>(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<const struct clang::Shape & S>}", "dyn_type": "const class clang::Circle &", "sub_classable": true }
// XX86-CHECK: "dynamic_types": [
// XX86-CHECK-NEXT: { "region": "SymRegion{reg_$0<const struct clang::Shape & S>}", "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<DEVICE Circle>(S);
clang_analyzer_printState();
// AMDGCN-CHECK: "dynamic_types": [
// AMDGCN-CHECK-NEXT: { "region": "SymRegion{reg_$0<const struct clang::Shape & S>}", "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<const struct clang::Shape & S>}", "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<DEVICE Circle>(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<const struct clang::Shape & S>}", "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<Circle>(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<DEVICE Circle>(S);
(void)C;
}
#endif
#endif
void evalNonNullParamNonNullReturnReference(const Shape &S) {