aboutsummaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer/Checkers/UninitializedObject
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-08-20 20:50:49 +0000
commit2298981669bf3bd63335a4be179bc0f96823a8f4 (patch)
tree1cbe2eb27f030d2d70b80ee5ca3c86bee7326a9f /lib/StaticAnalyzer/Checkers/UninitializedObject
parent9a83721404652cea39e9f02ae3e3b5c964602a5c (diff)
downloadsrc-2298981669bf3bd63335a4be179bc0f96823a8f4.tar.gz
src-2298981669bf3bd63335a4be179bc0f96823a8f4.zip
Vendor import of stripped clang trunk r366426 (just before thevendor/clang/clang-trunk-r366426
Notes
Notes: svn path=/vendor/clang/dist/; revision=351280 svn path=/vendor/clang/clang-trunk-r366426/; revision=351281; tag=vendor/clang/clang-trunk-r366426
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/UninitializedObject')
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h37
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp129
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp7
3 files changed, 137 insertions, 36 deletions
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
index c3291a21c164..2fcdd6086309 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
@@ -1,9 +1,8 @@
//===----- UninitializedObject.h ---------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,7 +17,7 @@
// won't emit warnings for objects that don't have at least one initialized
// field. This may be set with
//
-// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`.
+// `-analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true`.
//
// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
// warning for each uninitialized field, as opposed to emitting one warning
@@ -26,27 +25,34 @@
// to it in notes. Defaults to false.
//
// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
+// optin.cplusplus.UninitializedObject:NotesAsWarnings=true`.
//
// - "CheckPointeeInitialization" (boolean). If set to false, the checker will
// not analyze the pointee of pointer/reference fields, and will only check
// whether the object itself is initialized. Defaults to false.
//
// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
+// optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
+//
+// TODO: With some clever heuristics, some pointers should be dereferenced
+// by default. For example, if the pointee is constructed within the
+// constructor call, it's reasonable to say that no external object
+// references it, and we wouldn't generate multiple report on the same
+// pointee.
//
// - "IgnoreRecordsWithField" (string). If supplied, the checker will not
// analyze structures that have a field with a name or type name that
// matches the given pattern. Defaults to "".
//
// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
+// optin.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
//
-// TODO: With some clever heuristics, some pointers should be dereferenced
-// by default. For example, if the pointee is constructed within the
-// constructor call, it's reasonable to say that no external object
-// references it, and we wouldn't generate multiple report on the same
-// pointee.
+// - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze
+// _syntactically_ whether the found uninitialized object is used without a
+// preceding assert call. Defaults to false.
+//
+// `-analyzer-config \
+// optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`.
//
// Most of the following methods as well as the checker itself is defined in
// UninitializedObjectChecker.cpp.
@@ -69,6 +75,7 @@ struct UninitObjCheckerOptions {
bool ShouldConvertNotesToWarnings = false;
bool CheckPointeeInitialization = false;
std::string IgnoredRecordsWithFieldPattern;
+ bool IgnoreGuardedFields = false;
};
/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
@@ -316,8 +323,8 @@ private:
/// needs to be analyzed as much as checking whether their value is undefined.
inline bool isPrimitiveType(const QualType &T) {
return T->isBuiltinType() || T->isEnumeralType() ||
- T->isMemberPointerType() || T->isBlockPointerType() ||
- T->isFunctionType();
+ T->isFunctionType() || T->isAtomicType() ||
+ T->isVectorType() || T->isScalarType();
}
inline bool isDereferencableType(const QualType &T) {
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 208e303e8295..9d608c12d19b 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -1,9 +1,8 @@
//===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -20,6 +19,8 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "UninitializedObject.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -27,6 +28,7 @@
using namespace clang;
using namespace clang::ento;
+using namespace clang::ast_matchers;
/// We'll mark fields (and pointee of fields) that are confirmed to be
/// uninitialized as already analyzed.
@@ -119,6 +121,16 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
/// \p Pattern.
static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
+/// Checks _syntactically_ whether it is possible to access FD from the record
+/// that contains it without a preceding assert (even if that access happens
+/// inside a method). This is mainly used for records that act like unions, like
+/// having multiple bit fields, with only a fraction being properly initialized.
+/// If these fields are properly guarded with asserts, this method returns
+/// false.
+///
+/// Since this check is done syntactically, this method could be inaccurate.
+static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State);
+
//===----------------------------------------------------------------------===//
// Methods for UninitializedObjectChecker.
//===----------------------------------------------------------------------===//
@@ -235,6 +247,13 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
"One must also pass the pointee region as a parameter for "
"dereferenceable fields!");
+ if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
+ FR->getDecl()->getLocation()))
+ return false;
+
+ if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State))
+ return false;
+
if (State->contains<AnalyzedRegions>(FR))
return false;
@@ -247,13 +266,10 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
State = State->add<AnalyzedRegions>(FR);
- if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
- FR->getDecl()->getLocation()))
- return false;
-
UninitFieldMap::mapped_type NoteMsgBuf;
llvm::raw_svector_ostream OS(NoteMsgBuf);
Chain.printNoteMsg(OS);
+
return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second;
}
@@ -442,8 +458,8 @@ static const TypedValueRegion *
getConstructedRegion(const CXXConstructorDecl *CtorDecl,
CheckerContext &Context) {
- Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl,
- Context.getStackFrame());
+ Loc ThisLoc =
+ Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame());
SVal ObjectV = Context.getState()->getSVal(ThisLoc);
@@ -496,6 +512,75 @@ static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
return false;
}
+static const Stmt *getMethodBody(const CXXMethodDecl *M) {
+ if (isa<CXXConstructorDecl>(M))
+ return nullptr;
+
+ if (!M->isDefined())
+ return nullptr;
+
+ return M->getDefinition()->getBody();
+}
+
+static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) {
+
+ if (FD->getAccess() == AccessSpecifier::AS_public)
+ return true;
+
+ const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent());
+
+ if (!Parent)
+ return true;
+
+ Parent = Parent->getDefinition();
+ assert(Parent && "The record's definition must be avaible if an uninitialized"
+ " field of it was found!");
+
+ ASTContext &AC = State->getStateManager().getContext();
+
+ auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access");
+
+ auto AssertLikeM = callExpr(callee(functionDecl(
+ anyOf(hasName("exit"), hasName("panic"), hasName("error"),
+ hasName("Assert"), hasName("assert"), hasName("ziperr"),
+ hasName("assfail"), hasName("db_error"), hasName("__assert"),
+ hasName("__assert2"), hasName("_wassert"), hasName("__assert_rtn"),
+ hasName("__assert_fail"), hasName("dtrace_assfail"),
+ hasName("yy_fatal_error"), hasName("_XCAssertionFailureHandler"),
+ hasName("_DTAssertionFailureHandler"),
+ hasName("_TSAssertionFailureHandler")))));
+
+ auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn())));
+
+ auto GuardM =
+ stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM,
+ NoReturnFuncM))
+ .bind("guard");
+
+ for (const CXXMethodDecl *M : Parent->methods()) {
+ const Stmt *MethodBody = getMethodBody(M);
+ if (!MethodBody)
+ continue;
+
+ auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC);
+ if (Accesses.empty())
+ continue;
+ const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access");
+ assert(FirstAccess);
+
+ auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC);
+ if (Guards.empty())
+ return true;
+ const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard");
+ assert(FirstGuard);
+
+ if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc())
+ return true;
+ }
+
+ return false;
+}
+
std::string clang::ento::getVariableName(const FieldDecl *Field) {
// If Field is a captured lambda variable, Field->getName() will return with
// an empty string. We can however acquire it's name from the lambda's
@@ -526,13 +611,23 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
UninitObjCheckerOptions &ChOpts = Chk->Opts;
- ChOpts.IsPedantic =
- AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk);
- ChOpts.ShouldConvertNotesToWarnings =
- AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk);
+ ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");
+ ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption(
+ Chk, "NotesAsWarnings");
ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
- "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
+ Chk, "CheckPointeeInitialization");
ChOpts.IgnoredRecordsWithFieldPattern =
- AnOpts.getCheckerStringOption("IgnoreRecordsWithField",
- /*DefaultVal*/ "", Chk);
+ AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField");
+ ChOpts.IgnoreGuardedFields =
+ AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");
+
+ std::string ErrorMsg;
+ if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg))
+ Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField",
+ "a valid regex, building failed with error message "
+ "\"" + ErrorMsg + "\"");
+}
+
+bool ento::shouldRegisterUninitializedObjectChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index aead59c7bf87..a5dc250104f3 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -1,9 +1,8 @@
//===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//