aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-06-13 19:31:46 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-06-13 19:37:19 +0000
commite8d8bef961a50d4dc22501cde4fb9fb0be1b2532 (patch)
tree94f04805f47bb7c59ae29690d8952b6074fff602 /contrib/llvm-project/clang/lib/StaticAnalyzer
parentbb130ff39747b94592cb26d71b7cb097b9a4ea6b (diff)
parentb60736ec1405bb0a8dd40989f67ef4c93da068ab (diff)
downloadsrc-e8d8bef961a50d4dc22501cde4fb9fb0be1b2532.tar.gz
src-e8d8bef961a50d4dc22501cde4fb9fb0be1b2532.zip
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer')
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp10
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp69
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp312
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp103
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp5
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp271
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp48
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp514
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp2058
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp70
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp58
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp12
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp107
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp251
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp25
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp13
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp21
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp153
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp7
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp26
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp14
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp13
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp47
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp204
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp400
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp3
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp1114
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp41
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp23
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp48
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp16
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp19
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp28
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp49
-rw-r--r--contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp4
62 files changed, 4474 insertions, 1838 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 918c6e361381..a86a410ebcbc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -978,8 +978,7 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
ProgramStateRef State = C.getState();
// Check if this is the branch for the end of the loop.
- SVal CollectionSentinel = C.getSVal(FCS);
- if (CollectionSentinel.isZeroConstant()) {
+ if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index 528f68c6c429..131c1345af99 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -119,10 +119,10 @@ static const NoteTag *getNoteTag(CheckerContext &C,
Out << "Assuming ";
if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
- Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
+ Out << '\'' << DRE->getDecl()->getDeclName() << '\'';
} else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
Out << (IsKnownCast ? "Field '" : "field '")
- << ME->getMemberDecl()->getNameAsString() << '\'';
+ << ME->getMemberDecl()->getDeclName() << '\'';
} else {
Out << (IsKnownCast ? "The object" : "the object");
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 13836f08a61e..78b3c209ad6b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -406,7 +406,7 @@ ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
if (State->get<UnreleasedIvarMap>().isEmpty())
return State;
- auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
if (!CondBSE)
return State;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 03b7cbd1c833..7cdd78b8adfb 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -131,21 +131,21 @@ bool ento::shouldRegisterLiveVariablesDumper(const CheckerManager &mgr) {
//===----------------------------------------------------------------------===//
namespace {
-class LiveStatementsDumper : public Checker<check::ASTCodeBody> {
+class LiveExpressionsDumper : public Checker<check::ASTCodeBody> {
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
BugReporter &BR) const {
if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D))
- L->dumpStmtLiveness(Mgr.getSourceManager());
+ L->dumpExprLiveness(Mgr.getSourceManager());
}
};
}
-void ento::registerLiveStatementsDumper(CheckerManager &mgr) {
- mgr.registerChecker<LiveStatementsDumper>();
+void ento::registerLiveExpressionsDumper(CheckerManager &mgr) {
+ mgr.registerChecker<LiveExpressionsDumper>();
}
-bool ento::shouldRegisterLiveStatementsDumper(const CheckerManager &mgr) {
+bool ento::shouldRegisterLiveExpressionsDumper(const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index 2411f0e2d058..adfc2f8cb8fe 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -30,10 +30,14 @@ class DereferenceChecker
: public Checker< check::Location,
check::Bind,
EventDispatcher<ImplicitNullDerefEvent> > {
- mutable std::unique_ptr<BuiltinBug> BT_null;
- mutable std::unique_ptr<BuiltinBug> BT_undef;
+ enum DerefKind { NullPointer, UndefinedPointerValue };
- void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const;
+ BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
+ BugType BT_Undef{this, "Dereference of undefined pointer value",
+ categories::LogicError};
+
+ void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
+ CheckerContext &C) const;
public:
void checkLocation(SVal location, bool isLoad, const Stmt* S,
@@ -116,18 +120,29 @@ static bool isDeclRefExprToReference(const Expr *E) {
return false;
}
-void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
- CheckerContext &C) const {
+void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
+ const Stmt *S, CheckerContext &C) const {
+ const BugType *BT = nullptr;
+ llvm::StringRef DerefStr1;
+ llvm::StringRef DerefStr2;
+ switch (K) {
+ case DerefKind::NullPointer:
+ BT = &BT_Null;
+ DerefStr1 = " results in a null pointer dereference";
+ DerefStr2 = " results in a dereference of a null pointer";
+ break;
+ case DerefKind::UndefinedPointerValue:
+ BT = &BT_Undef;
+ DerefStr1 = " results in an undefined pointer dereference";
+ DerefStr2 = " results in a dereference of an undefined pointer value";
+ break;
+ };
+
// Generate an error node.
ExplodedNode *N = C.generateErrorNode(State);
if (!N)
return;
- // We know that 'location' cannot be non-null. This is what
- // we call an "explicit" null dereference.
- if (!BT_null)
- BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
-
SmallString<100> buf;
llvm::raw_svector_ostream os(buf);
@@ -139,7 +154,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
State.get(), N->getLocationContext());
- os << " results in a null pointer dereference";
+ os << DerefStr1;
break;
}
case Stmt::OMPArraySectionExprClass: {
@@ -147,11 +162,11 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
State.get(), N->getLocationContext());
- os << " results in a null pointer dereference";
+ os << DerefStr1;
break;
}
case Stmt::UnaryOperatorClass: {
- os << "Dereference of null pointer";
+ os << BT->getDescription();
const UnaryOperator *U = cast<UnaryOperator>(S);
AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
State.get(), N->getLocationContext(), true);
@@ -160,8 +175,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
case Stmt::MemberExprClass: {
const MemberExpr *M = cast<MemberExpr>(S);
if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
- os << "Access to field '" << M->getMemberNameInfo()
- << "' results in a dereference of a null pointer";
+ os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
State.get(), N->getLocationContext(), true);
}
@@ -169,8 +183,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
}
case Stmt::ObjCIvarRefExprClass: {
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
- os << "Access to instance variable '" << *IV->getDecl()
- << "' results in a dereference of a null pointer";
+ os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
State.get(), N->getLocationContext(), true);
break;
@@ -180,7 +193,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
}
auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
+ *BT, buf.empty() ? BT->getDescription() : StringRef(buf), N);
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
@@ -195,16 +208,9 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
CheckerContext &C) const {
// Check for dereference of an undefined value.
if (l.isUndef()) {
- if (ExplodedNode *N = C.generateErrorNode()) {
- if (!BT_undef)
- BT_undef.reset(
- new BuiltinBug(this, "Dereference of undefined pointer value"));
-
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_undef, BT_undef->getDescription(), N);
- bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
- C.emitReport(std::move(report));
- }
+ const Expr *DerefExpr = getDereferenceExpr(S);
+ if (!suppressReport(DerefExpr))
+ reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
return;
}
@@ -219,12 +225,13 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
ProgramStateRef notNullState, nullState;
std::tie(notNullState, nullState) = state->assume(location);
- // The explicit NULL case.
if (nullState) {
if (!notNullState) {
+ // 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)) {
- reportBug(nullState, expr, C);
+ reportBug(DerefKind::NullPointer, nullState, expr, C);
return;
}
}
@@ -266,7 +273,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
if (!StNonNull) {
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
if (!suppressReport(expr)) {
- reportBug(StNull, expr, C);
+ reportBug(DerefKind::NullPointer, StNull, expr, C);
return;
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 4225d890c47a..c0167b53ae26 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -7,11 +7,11 @@
//===----------------------------------------------------------------------===//
#include "Taint.h"
+#include "clang/Analysis/IssueHash.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h"
@@ -326,7 +326,7 @@ void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
const SourceManager &SM = C.getSourceManager();
FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
std::string HashContent =
- GetIssueString(SM, FL, getCheckerName().getName(), "Category",
+ getIssueString(FL, getCheckerName().getName(), "Category",
C.getLocationContext()->getDecl(), Opts);
reportBug(HashContent, C);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
index fc35082705fa..e3f4be0726c8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
@@ -20,29 +20,39 @@
// Art:
//
//
-// +-+---------v-+ +------------+
-// acquire_func succeeded | | Escape | |
-// +-----------------> Allocated +---------> Escaped <--+
-// | | | | | |
-// | +-----+------++ +------------+ |
-// | | | |
-// | release_func | +--+ |
-// | | | handle +--------+ |
-// | | | dies | | |
-// | +----v-----+ +---------> Leaked | |
-// | | | |(REPORT)| |
-// +----------+--+ | Released | Escape +--------+ |
-// | | | +---------------------------+
-// | Not tracked <--+ +----+---+-+
-// | | | | | As argument by value
-// +------+------+ | release_func | +------+ in function call
-// | | | | or by reference in
-// | | | | use_func call
-// +---------+ +----v-----+ | +-----------+
-// acquire_func failed | Double | +-----> Use after |
-// | released | | released |
-// | (REPORT) | | (REPORT) |
-// +----------+ +-----------+
+// +-------------+ +------------+
+// acquire_func succeeded | | Escape | |
+// +-----------------> Allocated +---------> Escaped <--+
+// | | | | | |
+// | +-----+------++ +------------+ |
+// | | | |
+// acquire_func | release_func | +--+ |
+// failed | | | handle +--------+ |
+// +---------+ | | | dies | | |
+// | | | +----v-----+ +---------> Leaked | |
+// | | | | | |(REPORT)| |
+// | +----------+--+ | Released | Escape +--------+ |
+// | | | | +---------------------------+
+// +--> Not tracked | +----+---+-+
+// | | | | As argument by value
+// +----------+--+ release_func | +------+ in function call
+// | | | or by reference in
+// | | | use_func call
+// unowned | +----v-----+ | +-----------+
+// acquire_func | | Double | +-----> Use after |
+// succeeded | | released | | released |
+// | | (REPORT) | | (REPORT) |
+// +---------------+ +----------+ +-----------+
+// | Allocated |
+// | Unowned | release_func
+// | +---------+
+// +---------------+ |
+// |
+// +-----v----------+
+// | Release of |
+// | unowned handle |
+// | (REPORT) |
+// +----------------+
//
// acquire_func represents the functions or syscalls that may acquire a handle.
// release_func represents the functions or syscalls that may release a handle.
@@ -53,7 +63,7 @@
//
// Note that, the analyzer does not always know for sure if a function failed
// or succeeded. In those cases we use the state MaybeAllocated.
-// Thus, the diagramm above captures the intent, not implementation details.
+// Thus, the diagram above captures the intent, not implementation details.
//
// Due to the fact that the number of handle related syscalls in Fuchsia
// is large, we adopt the annotation attributes to descript syscalls'
@@ -102,7 +112,7 @@ static const StringRef ErrorTypeName = "zx_status_t";
class HandleState {
private:
- enum class Kind { MaybeAllocated, Allocated, Released, Escaped } K;
+ enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K;
SymbolRef ErrorSym;
HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {}
@@ -114,6 +124,7 @@ public:
bool maybeAllocated() const { return K == Kind::MaybeAllocated; }
bool isReleased() const { return K == Kind::Released; }
bool isEscaped() const { return K == Kind::Escaped; }
+ bool isUnowned() const { return K == Kind::Unowned; }
static HandleState getMaybeAllocated(SymbolRef ErrorSym) {
return HandleState(Kind::MaybeAllocated, ErrorSym);
@@ -131,6 +142,9 @@ public:
static HandleState getEscaped() {
return HandleState(Kind::Escaped, nullptr);
}
+ static HandleState getUnowned() {
+ return HandleState(Kind::Unowned, nullptr);
+ }
SymbolRef getErrorSym() const { return ErrorSym; }
@@ -149,6 +163,7 @@ public:
CASE(Kind::Allocated)
CASE(Kind::Released)
CASE(Kind::Escaped)
+ CASE(Kind::Unowned)
}
if (ErrorSym) {
OS << " ErrorSym: ";
@@ -163,6 +178,11 @@ template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) {
return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia";
}
+template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) {
+ return D->hasAttr<Attr>() &&
+ D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned";
+}
+
class FuchsiaHandleChecker
: public Checker<check::PostCall, check::PreCall, check::DeadSymbols,
check::PointerEscape, eval::Assume> {
@@ -172,6 +192,8 @@ class FuchsiaHandleChecker
"Fuchsia Handle Error"};
BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release",
"Fuchsia Handle Error"};
+ BugType ReleaseUnownedBugType{
+ this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"};
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -190,6 +212,9 @@ public:
void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range,
CheckerContext &C) const;
+ void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range,
+ CheckerContext &C) const;
+
void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range,
CheckerContext &C) const;
@@ -226,32 +251,70 @@ static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym,
return nullptr;
}
-/// Returns the symbols extracted from the argument or null if it cannot be
-/// found.
-static SymbolRef getFuchsiaHandleSymbol(QualType QT, SVal Arg,
- ProgramStateRef State) {
+namespace {
+class FuchsiaHandleSymbolVisitor final : public SymbolVisitor {
+public:
+ FuchsiaHandleSymbolVisitor(ProgramStateRef State) : State(std::move(State)) {}
+ ProgramStateRef getState() const { return State; }
+
+ bool VisitSymbol(SymbolRef S) override {
+ if (const auto *HandleType = S->getType()->getAs<TypedefType>())
+ if (HandleType->getDecl()->getName() == HandleTypeName)
+ Symbols.push_back(S);
+ return true;
+ }
+
+ SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; }
+
+private:
+ SmallVector<SymbolRef, 1024> Symbols;
+ ProgramStateRef State;
+};
+} // end anonymous namespace
+
+/// Returns the symbols extracted from the argument or empty vector if it cannot
+/// be found. It is unlikely to have over 1024 symbols in one argument.
+static SmallVector<SymbolRef, 1024>
+getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) {
int PtrToHandleLevel = 0;
while (QT->isAnyPointerType() || QT->isReferenceType()) {
++PtrToHandleLevel;
QT = QT->getPointeeType();
}
+ if (QT->isStructureType()) {
+ // If we see a structure, see if there is any handle referenced by the
+ // structure.
+ FuchsiaHandleSymbolVisitor Visitor(State);
+ State->scanReachableSymbols(Arg, Visitor);
+ return Visitor.GetSymbols();
+ }
if (const auto *HandleType = QT->getAs<TypedefType>()) {
if (HandleType->getDecl()->getName() != HandleTypeName)
- return nullptr;
- if (PtrToHandleLevel > 1) {
+ return {};
+ if (PtrToHandleLevel > 1)
// Not supported yet.
- return nullptr;
- }
+ return {};
if (PtrToHandleLevel == 0) {
- return Arg.getAsSymbol();
+ SymbolRef Sym = Arg.getAsSymbol();
+ if (Sym) {
+ return {Sym};
+ } else {
+ return {};
+ }
} else {
assert(PtrToHandleLevel == 1);
- if (Optional<Loc> ArgLoc = Arg.getAs<Loc>())
- return State->getSVal(*ArgLoc).getAsSymbol();
+ if (Optional<Loc> ArgLoc = Arg.getAs<Loc>()) {
+ SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol();
+ if (Sym) {
+ return {Sym};
+ } else {
+ return {};
+ }
+ }
}
}
- return nullptr;
+ return {};
}
void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
@@ -273,31 +336,27 @@ void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call,
if (Arg >= FuncDecl->getNumParams())
break;
const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
- SymbolRef Handle =
- getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
- if (!Handle)
- continue;
+ SmallVector<SymbolRef, 1024> Handles =
+ getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
// Handled in checkPostCall.
if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) ||
hasFuchsiaAttr<AcquireHandleAttr>(PVD))
continue;
- const HandleState *HState = State->get<HStateMap>(Handle);
- if (!HState || HState->isEscaped())
- continue;
+ for (SymbolRef Handle : Handles) {
+ const HandleState *HState = State->get<HStateMap>(Handle);
+ if (!HState || HState->isEscaped())
+ continue;
- if (hasFuchsiaAttr<UseHandleAttr>(PVD) || PVD->getType()->isIntegerType()) {
- if (HState->isReleased()) {
- reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
- return;
+ if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
+ PVD->getType()->isIntegerType()) {
+ if (HState->isReleased()) {
+ reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C);
+ return;
+ }
}
}
- if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
- PVD->getType()->isIntegerType()) {
- // Working around integer by-value escapes.
- State = State->set<HStateMap>(Handle, HandleState::getEscaped());
- }
}
C.addTransition(State);
}
@@ -308,6 +367,10 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
if (!FuncDecl)
return;
+ // If we analyzed the function body, then ignore the annotations.
+ if (C.wasInlined)
+ return;
+
ProgramStateRef State = C.getState();
std::vector<std::function<std::string(BugReport & BR)>> Notes;
@@ -324,7 +387,7 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
- OS << "Function '" << FuncDecl->getNameAsString()
+ OS << "Function '" << FuncDecl->getDeclName()
<< "' returns an open handle";
return OS.str();
} else
@@ -332,6 +395,21 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
});
State =
State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr));
+ } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) {
+ // Function returns an unowned handle
+ SymbolRef RetSym = Call.getReturnValue().getAsSymbol();
+ Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(RetSym)) {
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Function '" << FuncDecl->getDeclName()
+ << "' returns an unowned handle";
+ return OS.str();
+ } else
+ return "";
+ });
+ State = State->set<HStateMap>(RetSym, HandleState::getUnowned());
}
for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
@@ -339,63 +417,88 @@ void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call,
break;
const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1;
- SymbolRef Handle =
- getFuchsiaHandleSymbol(PVD->getType(), Call.getArgSVal(Arg), State);
- if (!Handle)
- continue;
+ SmallVector<SymbolRef, 1024> Handles =
+ getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State);
- const HandleState *HState = State->get<HStateMap>(Handle);
- if (HState && HState->isEscaped())
- continue;
- if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
- if (HState && HState->isReleased()) {
- reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
- return;
- } else {
+ for (SymbolRef Handle : Handles) {
+ const HandleState *HState = State->get<HStateMap>(Handle);
+ if (HState && HState->isEscaped())
+ continue;
+ if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
+ if (HState && HState->isReleased()) {
+ reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C);
+ return;
+ } else if (HState && HState->isUnowned()) {
+ reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C);
+ return;
+ } else {
+ Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Handle released through " << ParamDiagIdx
+ << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
+ return OS.str();
+ } else
+ return "";
+ });
+ State = State->set<HStateMap>(Handle, HandleState::getReleased());
+ }
+ } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
std::string SBuf;
llvm::raw_string_ostream OS(SBuf);
- OS << "Handle released through " << ParamDiagIdx
+ OS << "Handle allocated through " << ParamDiagIdx
<< llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
return OS.str();
} else
return "";
});
- State = State->set<HStateMap>(Handle, HandleState::getReleased());
+ State = State->set<HStateMap>(
+ Handle, HandleState::getMaybeAllocated(ResultSymbol));
+ } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)) {
+ Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
+ auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
+ if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
+ std::string SBuf;
+ llvm::raw_string_ostream OS(SBuf);
+ OS << "Unowned handle allocated through " << ParamDiagIdx
+ << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
+ return OS.str();
+ } else
+ return "";
+ });
+ State = State->set<HStateMap>(Handle, HandleState::getUnowned());
+ } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) &&
+ PVD->getType()->isIntegerType()) {
+ // Working around integer by-value escapes.
+ // The by-value escape would not be captured in checkPointerEscape.
+ // If the function was not analyzed (otherwise wasInlined should be
+ // true) and there is no annotation on the handle, we assume the handle
+ // is escaped.
+ State = State->set<HStateMap>(Handle, HandleState::getEscaped());
}
- } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) {
- Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string {
- auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR);
- if (auto IsInteresting = PathBR->getInterestingnessKind(Handle)) {
- std::string SBuf;
- llvm::raw_string_ostream OS(SBuf);
- OS << "Handle allocated through " << ParamDiagIdx
- << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter";
- return OS.str();
- } else
- return "";
- });
- State = State->set<HStateMap>(
- Handle, HandleState::getMaybeAllocated(ResultSymbol));
}
}
const NoteTag *T = nullptr;
if (!Notes.empty()) {
T = C.getNoteTag([this, Notes{std::move(Notes)}](
PathSensitiveBugReport &BR) -> std::string {
- if (&BR.getBugType() != &UseAfterReleaseBugType &&
- &BR.getBugType() != &LeakBugType &&
- &BR.getBugType() != &DoubleReleaseBugType)
- return "";
- for (auto &Note : Notes) {
- std::string Text = Note(BR);
- if (!Text.empty())
- return Text;
- }
- return "";
- });
+ if (&BR.getBugType() != &UseAfterReleaseBugType &&
+ &BR.getBugType() != &LeakBugType &&
+ &BR.getBugType() != &DoubleReleaseBugType &&
+ &BR.getBugType() != &ReleaseUnownedBugType)
+ return "";
+ for (auto &Note : Notes) {
+ std::string Text = Note(BR);
+ if (!Text.empty())
+ return Text;
+ }
+ return "";
+ });
}
C.addTransition(State, T);
}
@@ -481,13 +584,14 @@ ProgramStateRef FuchsiaHandleChecker::checkPointerEscape(
if (Arg >= FuncDecl->getNumParams())
break;
const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg);
- SymbolRef Handle =
- getFuchsiaHandleSymbol(PVD->getType(), Call->getArgSVal(Arg), State);
- if (!Handle)
- continue;
- if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
- hasFuchsiaAttr<ReleaseHandleAttr>(PVD))
- UnEscaped.insert(Handle);
+ SmallVector<SymbolRef, 1024> Handles =
+ getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State);
+ for (SymbolRef Handle : Handles) {
+ if (hasFuchsiaAttr<UseHandleAttr>(PVD) ||
+ hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) {
+ UnEscaped.insert(Handle);
+ }
+ }
}
}
@@ -525,6 +629,14 @@ void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym,
"Releasing a previously released handle");
}
+void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym,
+ const SourceRange &Range,
+ CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateErrorNode(C.getState());
+ reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType,
+ "Releasing an unowned handle");
+}
+
void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym,
const SourceRange &Range,
CheckerContext &C) const {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index c06d2fcd8e7d..42c777eb2c52 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -103,6 +103,9 @@ private:
struct FunctionData {
FunctionData() = delete;
+ FunctionData(const FunctionDecl *FDecl, StringRef Name,
+ std::string FullName)
+ : FDecl(FDecl), Name(Name), FullName(std::move(FullName)) {}
FunctionData(const FunctionData &) = default;
FunctionData(FunctionData &&) = default;
FunctionData &operator=(const FunctionData &) = delete;
@@ -123,7 +126,7 @@ private:
if (Name.empty() || FullName.empty())
return None;
- return FunctionData{FDecl, Name, FullName};
+ return FunctionData{FDecl, Name, std::move(FullName)};
}
bool isInScope(StringRef Scope) const {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
index fd8cbd694b24..ab5e6a1c9991 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -109,7 +109,7 @@ class IteratorModeling
bool Postfix) const;
void handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
OverloadedOperatorKind Op, const SVal &RetVal,
- const SVal &LHS, const SVal &RHS) const;
+ const SVal &Iterator, const SVal &Amount) const;
void handlePtrIncrOrDecr(CheckerContext &C, const Expr *Iterator,
OverloadedOperatorKind OK, SVal Offset) const;
void handleAdvance(CheckerContext &C, const Expr *CE, SVal RetVal, SVal Iter,
@@ -262,18 +262,30 @@ void IteratorModeling::checkPostStmt(const UnaryOperator *UO,
void IteratorModeling::checkPostStmt(const BinaryOperator *BO,
CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- BinaryOperatorKind OK = BO->getOpcode();
- SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
+ const ProgramStateRef State = C.getState();
+ const BinaryOperatorKind OK = BO->getOpcode();
+ const Expr *const LHS = BO->getLHS();
+ const Expr *const RHS = BO->getRHS();
+ const SVal LVal = State->getSVal(LHS, C.getLocationContext());
+ const SVal RVal = State->getSVal(RHS, C.getLocationContext());
if (isSimpleComparisonOperator(BO->getOpcode())) {
- SVal LVal = State->getSVal(BO->getLHS(), C.getLocationContext());
SVal Result = State->getSVal(BO, C.getLocationContext());
handleComparison(C, BO, Result, LVal, RVal,
BinaryOperator::getOverloadedOperator(OK));
} else if (isRandomIncrOrDecrOperator(OK)) {
- handlePtrIncrOrDecr(C, BO->getLHS(),
- BinaryOperator::getOverloadedOperator(OK), RVal);
+ // In case of operator+ the iterator can be either on the LHS (eg.: it + 1),
+ // or on the RHS (eg.: 1 + it). Both cases are modeled.
+ const bool IsIterOnLHS = BO->getLHS()->getType()->isPointerType();
+ const Expr *const &IterExpr = IsIterOnLHS ? LHS : RHS;
+ const Expr *const &AmountExpr = IsIterOnLHS ? RHS : LHS;
+
+ // The non-iterator side must have an integral or enumeration type.
+ if (!AmountExpr->getType()->isIntegralOrEnumerationType())
+ return;
+ const SVal &AmountVal = IsIterOnLHS ? RVal : LVal;
+ handlePtrIncrOrDecr(C, IterExpr, BinaryOperator::getOverloadedOperator(OK),
+ AmountVal);
}
}
@@ -366,11 +378,24 @@ IteratorModeling::handleOverloadedOperator(CheckerContext &C,
InstCall->getCXXThisVal(), Call.getArgSVal(0));
return;
}
- } else {
- if (Call.getNumArgs() >= 2 &&
- Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) {
+ } else if (Call.getNumArgs() >= 2) {
+ const Expr *FirstArg = Call.getArgExpr(0);
+ const Expr *SecondArg = Call.getArgExpr(1);
+ const QualType FirstType = FirstArg->getType();
+ const QualType SecondType = SecondArg->getType();
+
+ if (FirstType->isIntegralOrEnumerationType() ||
+ SecondType->isIntegralOrEnumerationType()) {
+ // In case of operator+ the iterator can be either on the LHS (eg.:
+ // it + 1), or on the RHS (eg.: 1 + it). Both cases are modeled.
+ const bool IsIterFirst = FirstType->isStructureOrClassType();
+ const SVal FirstArg = Call.getArgSVal(0);
+ const SVal SecondArg = Call.getArgSVal(1);
+ const SVal &Iterator = IsIterFirst ? FirstArg : SecondArg;
+ const SVal &Amount = IsIterFirst ? SecondArg : FirstArg;
+
handleRandomIncrOrDecr(C, OrigExpr, Op, Call.getReturnValue(),
- Call.getArgSVal(0), Call.getArgSVal(1));
+ Iterator, Amount);
return;
}
}
@@ -461,6 +486,12 @@ void IteratorModeling::handleComparison(CheckerContext &C, const Expr *CE,
RPos = getIteratorPosition(State, RVal);
}
+ // If the value for which we just tried to set a new iterator position is
+ // an `SVal`for which no iterator position can be set then the setting was
+ // unsuccessful. We cannot handle the comparison in this case.
+ if (!LPos || !RPos)
+ return;
+
// We cannot make assumptions on `UnknownVal`. Let us conjure a symbol
// instead.
if (RetVal.isUnknown()) {
@@ -556,35 +587,35 @@ void IteratorModeling::handleDecrement(CheckerContext &C, const SVal &RetVal,
C.addTransition(State);
}
-void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C,
- const Expr *CE,
+void IteratorModeling::handleRandomIncrOrDecr(CheckerContext &C, const Expr *CE,
OverloadedOperatorKind Op,
const SVal &RetVal,
- const SVal &LHS,
- const SVal &RHS) const {
+ const SVal &Iterator,
+ const SVal &Amount) const {
// Increment or decrement the symbolic expressions which represents the
// position of the iterator
auto State = C.getState();
- const auto *Pos = getIteratorPosition(State, LHS);
+ const auto *Pos = getIteratorPosition(State, Iterator);
if (!Pos)
return;
- const auto *value = &RHS;
- SVal val;
- if (auto loc = RHS.getAs<Loc>()) {
- val = State->getRawSVal(*loc);
- value = &val;
+ const auto *Value = &Amount;
+ SVal Val;
+ if (auto LocAmount = Amount.getAs<Loc>()) {
+ Val = State->getRawSVal(*LocAmount);
+ Value = &Val;
}
- auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
+ const auto &TgtVal =
+ (Op == OO_PlusEqual || Op == OO_MinusEqual) ? Iterator : RetVal;
// `AdvancedState` is a state where the position of `LHS` is advanced. We
// only need this state to retrieve the new position, but we do not want
// to change the position of `LHS` (in every case).
- auto AdvancedState = advancePosition(State, LHS, Op, *value);
+ auto AdvancedState = advancePosition(State, Iterator, Op, *Value);
if (AdvancedState) {
- const auto *NewPos = getIteratorPosition(AdvancedState, LHS);
+ const auto *NewPos = getIteratorPosition(AdvancedState, Iterator);
assert(NewPos &&
"Iterator should have position after successful advancement");
@@ -599,6 +630,9 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
const Expr *Iterator,
OverloadedOperatorKind OK,
SVal Offset) const {
+ if (!Offset.getAs<DefinedSVal>())
+ return;
+
QualType PtrType = Iterator->getType();
if (!PtrType->isPointerType())
return;
@@ -612,13 +646,11 @@ void IteratorModeling::handlePtrIncrOrDecr(CheckerContext &C,
return;
SVal NewVal;
- if (OK == OO_Plus || OK == OO_PlusEqual)
+ if (OK == OO_Plus || OK == OO_PlusEqual) {
NewVal = State->getLValue(ElementType, Offset, OldVal);
- else {
- const llvm::APSInt &OffsetInt =
- Offset.castAs<nonloc::ConcreteInt>().getValue();
- auto &BVF = C.getSymbolManager().getBasicVals();
- SVal NegatedOffset = nonloc::ConcreteInt(BVF.getValue(-OffsetInt));
+ } else {
+ auto &SVB = C.getSValBuilder();
+ SVal NegatedOffset = SVB.evalMinus(Offset.castAs<NonLoc>());
NewVal = State->getLValue(ElementType, NegatedOffset, OldVal);
}
@@ -684,9 +716,14 @@ bool IteratorModeling::noChangeInAdvance(CheckerContext &C, SVal Iter,
const auto StateBefore = N->getState();
const auto *PosBefore = getIteratorPosition(StateBefore, Iter);
-
- assert(PosBefore && "`std::advance() should not create new iterator "
- "position but change existing ones");
+ // FIXME: `std::advance()` should not create a new iterator position but
+ // change existing ones. However, in case of iterators implemented as
+ // pointers the handling of parameters in `std::advance()`-like
+ // functions is still incomplete which may result in cases where
+ // the new position is assigned to the wrong pointer. This causes
+ // crash if we use an assertion here.
+ if (!PosBefore)
+ return false;
return PosBefore->getOffset() == PosAfter->getOffset();
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
index df8e379d1f20..dd014648eb6f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -169,6 +169,8 @@ void IteratorRangeChecker::checkPreStmt(const BinaryOperator *BO,
verifyDereference(C, LVal);
} else if (isRandomIncrOrDecrOperator(OK)) {
SVal RVal = State->getSVal(BO->getRHS(), C.getLocationContext());
+ if (!BO->getRHS()->getType()->isIntegralOrEnumerationType())
+ return;
verifyRandomIncrOrDecr(C, BinaryOperator::getOverloadedOperator(OK), LVal,
RVal);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 252377f24bd7..28d3e058fee2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -1141,10 +1141,9 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);
}
- bool Invalid = false;
- const llvm::MemoryBuffer *BF =
- Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid);
- if (Invalid)
+ llvm::Optional<llvm::MemoryBufferRef> BF =
+ Mgr.getSourceManager().getBufferOrNone(SLInfo.first, SL);
+ if (!BF)
return;
Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(),
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index 87477e96d2d1..a157ee2da5df 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -509,7 +509,7 @@ ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
if (AMap.isEmpty())
return State;
- auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymbol());
if (!CondBSE)
return State;
BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index d5b0a5b2220f..f117d5505ecb 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -3110,11 +3110,6 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
return true;
}
- if (FName == "postEvent" &&
- FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") {
- return true;
- }
-
if (FName == "connectImpl" &&
FD->getQualifiedNameAsString() == "QObject::connectImpl") {
return true;
@@ -3301,14 +3296,16 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
OS << "reallocated by call to '";
const Stmt *S = RSCurr->getStmt();
if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
- OS << MemCallE->getMethodDecl()->getNameAsString();
+ OS << MemCallE->getMethodDecl()->getDeclName();
} else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
- OS << OpCallE->getDirectCallee()->getNameAsString();
+ OS << OpCallE->getDirectCallee()->getDeclName();
} else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
auto &CEMgr = BRC.getStateManager().getCallEventManager();
CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
- const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl());
- OS << (D ? D->getNameAsString() : "unknown");
+ if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
+ OS << D->getDeclName();
+ else
+ OS << "unknown";
}
OS << "'";
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index 7f0519c695b0..a38298a7abed 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -104,7 +104,7 @@ private:
"basic_ios",
"future",
"optional",
- "packaged_task"
+ "packaged_task",
"promise",
"shared_future",
"shared_lock",
@@ -580,7 +580,7 @@ void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
if (const auto DR =
dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) {
const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
- OS << " '" << RegionDecl->getNameAsString() << "'";
+ OS << " '" << RegionDecl->getDeclName() << "'";
}
ObjectKind OK = classifyObject(MR, RD);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
index 53ed0e187a4c..270b66dab020 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
@@ -24,32 +24,36 @@ using namespace ento;
using namespace ast_matchers;
namespace {
-
-const char *WarnAtNode = "OSObjCast";
+static constexpr const char *const WarnAtNode = "WarnAtNode";
+static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
public:
- void checkASTCodeBody(const Decl *D,
- AnalysisManager &AM,
+ void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
BugReporter &BR) const;
};
+}
static void emitDiagnostics(const BoundNodes &Nodes,
BugReporter &BR,
AnalysisDeclContext *ADC,
const OSObjectCStyleCastChecker *Checker) {
const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
- assert(CE);
+ const CXXRecordDecl *RD = Nodes.getNodeAs<CXXRecordDecl>(WarnRecordDecl);
+ assert(CE && RD);
std::string Diagnostics;
llvm::raw_string_ostream OS(Diagnostics);
- OS << "C-style cast of OSObject. Use OSDynamicCast instead.";
+ OS << "C-style cast of an OSObject is prone to type confusion attacks; "
+ << "use 'OSRequiredCast' if the object is definitely of type '"
+ << RD->getNameAsString() << "', or 'OSDynamicCast' followed by "
+ << "a null check if unsure",
BR.EmitBasicReport(
ADC->getDecl(),
Checker,
/*Name=*/"OSObject C-Style Cast",
- /*BugCategory=*/"Security",
+ categories::SecurityError,
OS.str(),
PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
CE->getSourceRange());
@@ -68,7 +72,7 @@ void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager
auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
auto OSObjSubclassM = hasTypePointingTo(
- cxxRecordDecl(isDerivedFrom("OSObject")));
+ cxxRecordDecl(isDerivedFrom("OSObject")).bind(WarnRecordDecl));
auto CastM = cStyleCastExpr(
allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
@@ -78,7 +82,6 @@ void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager
for (BoundNodes Match : Matches)
emitDiagnostics(Match, BR, ADC, this);
}
-}
void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
Mgr.registerChecker<OSObjectCStyleCastChecker>();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index 24e2a4dea922..35a600f2d7b8 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -21,7 +21,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
-#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/raw_ostream.h"
@@ -74,7 +74,7 @@ private:
void initializeSelectors(ASTContext &Ctx) const;
void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
StringRef ClassName) const;
- mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
+ mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass;
mutable bool IsInitialized;
};
@@ -100,7 +100,8 @@ bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
ArrayRef<SelectorDescriptor> Sel,
StringRef ClassName) const {
- llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
+ llvm::SmallPtrSet<Selector, 16> &ClassSelectors =
+ SelectorsForClass[ClassName];
// Fill the Selectors SmallSet with all selectors we want to check.
for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
I != E; ++I) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 0b00664c7c10..96f0d9bb3c3d 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -248,8 +248,9 @@ public:
FieldInfo RetVal;
RetVal.Field = FD;
auto &Ctx = FD->getASTContext();
- std::tie(RetVal.Size, RetVal.Align) =
- Ctx.getTypeInfoInChars(FD->getType());
+ auto Info = Ctx.getTypeInfoInChars(FD->getType());
+ RetVal.Size = Info.Width;
+ RetVal.Align = Info.Align;
assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
if (auto Max = FD->getMaxAlignment())
RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 285d2da104f1..88e80c481a5a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -83,7 +83,7 @@ public:
private:
typedef void (PthreadLockChecker::*FnCheck)(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
CallDescriptionMap<FnCheck> PThreadCallbacks = {
// Init.
{{"pthread_mutex_init", 2}, &PthreadLockChecker::InitAnyLock},
@@ -167,46 +167,49 @@ private:
ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
const MemRegion *lockR,
const SymbolRef *sym) const;
- void reportUseDestroyedBug(const CallEvent &Call, CheckerContext &C,
- unsigned ArgNo, CheckerKind checkKind) const;
+ void reportBug(CheckerContext &C, std::unique_ptr<BugType> BT[],
+ const Expr *MtxExpr, CheckerKind CheckKind,
+ StringRef Desc) const;
// Init.
void InitAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void InitLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal Lock, CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void InitLockAux(const CallEvent &Call, CheckerContext &C,
+ const Expr *MtxExpr, SVal MtxVal,
+ CheckerKind CheckKind) const;
// Lock, Try-lock.
void AcquirePthreadLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void AcquireXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryPthreadLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryFuchsiaLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void TryC11Lock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void AcquireLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock, bool isTryLock, LockingSemantics semantics,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void AcquireLockAux(const CallEvent &Call, CheckerContext &C,
+ const Expr *MtxExpr, SVal MtxVal, bool IsTryLock,
+ LockingSemantics Semantics, CheckerKind CheckKind) const;
// Release.
void ReleaseAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void ReleaseLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal lock, CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void ReleaseLockAux(const CallEvent &Call, CheckerContext &C,
+ const Expr *MtxExpr, SVal MtxVal,
+ CheckerKind CheckKind) const;
// Destroy.
void DestroyPthreadLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
void DestroyXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkkind) const;
- void DestroyLockAux(const CallEvent &Call, CheckerContext &C, unsigned ArgNo,
- SVal Lock, LockingSemantics semantics,
- CheckerKind checkkind) const;
+ CheckerKind CheckKind) const;
+ void DestroyLockAux(const CallEvent &Call, CheckerContext &C,
+ const Expr *MtxExpr, SVal MtxVal,
+ LockingSemantics Semantics, CheckerKind CheckKind) const;
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
@@ -226,18 +229,18 @@ private:
mutable std::unique_ptr<BugType> BT_initlock[CK_NumCheckKinds];
mutable std::unique_ptr<BugType> BT_lor[CK_NumCheckKinds];
- void initBugType(CheckerKind checkKind) const {
- if (BT_doublelock[checkKind])
+ void initBugType(CheckerKind CheckKind) const {
+ if (BT_doublelock[CheckKind])
return;
- BT_doublelock[checkKind].reset(
- new BugType{CheckNames[checkKind], "Double locking", "Lock checker"});
- BT_doubleunlock[checkKind].reset(
- new BugType{CheckNames[checkKind], "Double unlocking", "Lock checker"});
- BT_destroylock[checkKind].reset(new BugType{
- CheckNames[checkKind], "Use destroyed lock", "Lock checker"});
- BT_initlock[checkKind].reset(new BugType{
- CheckNames[checkKind], "Init invalid lock", "Lock checker"});
- BT_lor[checkKind].reset(new BugType{CheckNames[checkKind],
+ BT_doublelock[CheckKind].reset(
+ new BugType{CheckNames[CheckKind], "Double locking", "Lock checker"});
+ BT_doubleunlock[CheckKind].reset(
+ new BugType{CheckNames[CheckKind], "Double unlocking", "Lock checker"});
+ BT_destroylock[CheckKind].reset(new BugType{
+ CheckNames[CheckKind], "Use destroyed lock", "Lock checker"});
+ BT_initlock[CheckKind].reset(new BugType{
+ CheckNames[CheckKind], "Init invalid lock", "Lock checker"});
+ BT_lor[CheckKind].reset(new BugType{CheckNames[CheckKind],
"Lock order reversal", "Lock checker"});
}
};
@@ -341,53 +344,53 @@ void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
void PthreadLockChecker::AcquirePthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::AcquireXNULock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), false, XNUSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), false,
+ XNUSemantics, CheckKind);
}
void PthreadLockChecker::TryPthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryXNULock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryFuchsiaLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::TryC11Lock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- AcquireLockAux(Call, C, 0, Call.getArgSVal(0), true, PthreadSemantics,
- checkKind);
+ CheckerKind CheckKind) const {
+ AcquireLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), true,
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
- CheckerContext &C, unsigned ArgNo,
- SVal lock, bool isTryLock,
- enum LockingSemantics semantics,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerContext &C, const Expr *MtxExpr,
+ SVal MtxVal, bool IsTryLock,
+ enum LockingSemantics Semantics,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *lockR = lock.getAsRegion();
+ const MemRegion *lockR = MtxVal.getAsRegion();
if (!lockR)
return;
@@ -398,28 +401,23 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isLocked()) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_doublelock[checkKind], "This lock has already been acquired", N);
- report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(report));
+ reportBug(C, BT_doublelock, MtxExpr, CheckKind,
+ "This lock has already been acquired");
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+ reportBug(C, BT_destroylock, MtxExpr, CheckKind,
+ "This lock has already been destroyed");
return;
}
}
ProgramStateRef lockSucc = state;
- if (isTryLock) {
+ if (IsTryLock) {
// Bifurcate the state, and allow a mode where the lock acquisition fails.
SVal RetVal = Call.getReturnValue();
if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
ProgramStateRef lockFail;
- switch (semantics) {
+ switch (Semantics) {
case PthreadSemantics:
std::tie(lockFail, lockSucc) = state->assume(*DefinedRetVal);
break;
@@ -434,7 +432,7 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
}
// We might want to handle the case when the mutex lock function was inlined
// and returned an Unknown or Undefined value.
- } else if (semantics == PthreadSemantics) {
+ } else if (Semantics == PthreadSemantics) {
// Assume that the return value was 0.
SVal RetVal = Call.getReturnValue();
if (auto DefinedRetVal = RetVal.getAs<DefinedSVal>()) {
@@ -447,7 +445,7 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
// and returned an Unknown or Undefined value.
} else {
// XNU locking semantics return void on non-try locks
- assert((semantics == XNUSemantics) && "Unknown locking semantics");
+ assert((Semantics == XNUSemantics) && "Unknown locking semantics");
lockSucc = state;
}
@@ -459,18 +457,18 @@ void PthreadLockChecker::AcquireLockAux(const CallEvent &Call,
void PthreadLockChecker::ReleaseAnyLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- ReleaseLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+ CheckerKind CheckKind) const {
+ ReleaseLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
}
void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
- CheckerContext &C, unsigned ArgNo,
- SVal lock,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerContext &C, const Expr *MtxExpr,
+ SVal MtxVal,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *lockR = lock.getAsRegion();
+ const MemRegion *lockR = MtxVal.getAsRegion();
if (!lockR)
return;
@@ -481,18 +479,12 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
if (const LockState *LState = state->get<LockMap>(lockR)) {
if (LState->isUnlocked()) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_doubleunlock[checkKind], "This lock has already been unlocked",
- N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ reportBug(C, BT_doubleunlock, MtxExpr, CheckKind,
+ "This lock has already been unlocked");
return;
} else if (LState->isDestroyed()) {
- reportUseDestroyedBug(Call, C, ArgNo, checkKind);
+ reportBug(C, BT_destroylock, MtxExpr, CheckKind,
+ "This lock has already been destroyed");
return;
}
}
@@ -502,17 +494,9 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
if (!LS.isEmpty()) {
const MemRegion *firstLockR = LS.getHead();
if (firstLockR != lockR) {
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto report = std::make_unique<PathSensitiveBugReport>(
- *BT_lor[checkKind],
- "This was not the most recently acquired lock. Possible "
- "lock order reversal",
- N);
- report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(report));
+ reportBug(C, BT_lor, MtxExpr, CheckKind,
+ "This was not the most recently acquired lock. Possible lock "
+ "order reversal");
return;
}
// Record that the lock was released.
@@ -525,25 +509,27 @@ void PthreadLockChecker::ReleaseLockAux(const CallEvent &Call,
void PthreadLockChecker::DestroyPthreadLock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), PthreadSemantics, checkKind);
+ CheckerKind CheckKind) const {
+ DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0),
+ PthreadSemantics, CheckKind);
}
void PthreadLockChecker::DestroyXNULock(const CallEvent &Call,
CheckerContext &C,
- CheckerKind checkKind) const {
- DestroyLockAux(Call, C, 0, Call.getArgSVal(0), XNUSemantics, checkKind);
+ CheckerKind CheckKind) const {
+ DestroyLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), XNUSemantics,
+ CheckKind);
}
void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
- CheckerContext &C, unsigned ArgNo,
- SVal Lock,
- enum LockingSemantics semantics,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ CheckerContext &C, const Expr *MtxExpr,
+ SVal MtxVal,
+ enum LockingSemantics Semantics,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *LockR = Lock.getAsRegion();
+ const MemRegion *LockR = MtxVal.getAsRegion();
if (!LockR)
return;
@@ -556,7 +542,7 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
const LockState *LState = State->get<LockMap>(LockR);
// Checking the return value of the destroy method only in the case of
// PthreadSemantics
- if (semantics == PthreadSemantics) {
+ if (Semantics == PthreadSemantics) {
if (!LState || LState->isUnlocked()) {
SymbolRef sym = Call.getReturnValue().getAsSymbol();
if (!sym) {
@@ -581,36 +567,26 @@ void PthreadLockChecker::DestroyLockAux(const CallEvent &Call,
return;
}
}
- StringRef Message;
- if (LState->isLocked()) {
- Message = "This lock is still locked";
- } else {
- Message = "This lock has already been destroyed";
- }
+ StringRef Message = LState->isLocked()
+ ? "This lock is still locked"
+ : "This lock has already been destroyed";
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_destroylock[checkKind], Message, N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ reportBug(C, BT_destroylock, MtxExpr, CheckKind, Message);
}
void PthreadLockChecker::InitAnyLock(const CallEvent &Call, CheckerContext &C,
- CheckerKind checkKind) const {
- InitLockAux(Call, C, 0, Call.getArgSVal(0), checkKind);
+ CheckerKind CheckKind) const {
+ InitLockAux(Call, C, Call.getArgExpr(0), Call.getArgSVal(0), CheckKind);
}
void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
- unsigned ArgNo, SVal Lock,
- CheckerKind checkKind) const {
- if (!ChecksEnabled[checkKind])
+ const Expr *MtxExpr, SVal MtxVal,
+ CheckerKind CheckKind) const {
+ if (!ChecksEnabled[CheckKind])
return;
- const MemRegion *LockR = Lock.getAsRegion();
+ const MemRegion *LockR = MtxVal.getAsRegion();
if (!LockR)
return;
@@ -627,35 +603,24 @@ void PthreadLockChecker::InitLockAux(const CallEvent &Call, CheckerContext &C,
return;
}
- StringRef Message;
-
- if (LState->isLocked()) {
- Message = "This lock is still being held";
- } else {
- Message = "This lock has already been initialized";
- }
+ StringRef Message = LState->isLocked()
+ ? "This lock is still being held"
+ : "This lock has already been initialized";
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_initlock[checkKind], Message, N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
- C.emitReport(std::move(Report));
+ reportBug(C, BT_initlock, MtxExpr, CheckKind, Message);
}
-void PthreadLockChecker::reportUseDestroyedBug(const CallEvent &Call,
- CheckerContext &C,
- unsigned ArgNo,
- CheckerKind checkKind) const {
+void PthreadLockChecker::reportBug(CheckerContext &C,
+ std::unique_ptr<BugType> BT[],
+ const Expr *MtxExpr, CheckerKind CheckKind,
+ StringRef Desc) const {
ExplodedNode *N = C.generateErrorNode();
if (!N)
return;
- initBugType(checkKind);
- auto Report = std::make_unique<PathSensitiveBugReport>(
- *BT_destroylock[checkKind], "This lock has already been destroyed", N);
- Report->addRange(Call.getArgExpr(ArgNo)->getSourceRange());
+ initBugType(CheckKind);
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT[CheckKind], Desc, N);
+ Report->addRange(MtxExpr->getSourceRange());
C.emitReport(std::move(Report));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 1d8ed90f7590..1d903530201f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -177,7 +177,7 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
- if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
+ if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymbol() == Sym)
return Idx;
return None;
@@ -439,7 +439,7 @@ annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
std::string s;
llvm::raw_string_ostream os(s);
- os << "Parameter '" << PVD->getNameAsString() << "' starts at +";
+ os << "Parameter '" << PVD->getDeclName() << "' starts at +";
if (CurrT->getCount() == 1) {
os << "1, as it is marked as consuming";
} else {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 599d4f306aa1..1a94ccdc2825 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -58,6 +58,11 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
state, ER->getSuperRegion(), C.getSValBuilder(), ER->getValueType());
+ // We assume that the location after the last element in the array is used as
+ // end() iterator. Reporting on these would return too many false positives.
+ if (Idx == ElementCount)
+ return;
+
ProgramStateRef StInBound = state->assumeInBound(Idx, ElementCount, true);
ProgramStateRef StOutBound = state->assumeInBound(Idx, ElementCount, false);
if (StOutBound && !StInBound) {
@@ -70,7 +75,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
// types explicitly reference such exploit categories (when applicable).
if (!BT)
BT.reset(new BuiltinBug(
- this, "Return of pointer value outside of expected range",
+ this, "Buffer overflow",
"Returned pointer value points outside the original object "
"(potential buffer overflow)"));
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
index ec43a23e30a9..92c386bbb2b0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
@@ -26,6 +26,8 @@ bool isStdSmartPtrCall(const CallEvent &Call);
/// Returns whether the smart pointer is null or not.
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);
+const BugType *getNullDereferenceBugType();
+
} // namespace smartptr
} // namespace ento
} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
index 7bb25f397d01..8a85d454856b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
@@ -23,23 +23,40 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/StringRef.h"
using namespace clang;
using namespace ento;
namespace {
-class SmartPtrChecker : public Checker<check::PreCall> {
- BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
- "C++ Smart Pointer"};
+static const BugType *NullDereferenceBugTypePtr;
+
+class SmartPtrChecker : public Checker<check::PreCall> {
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
+ "C++ Smart Pointer"};
private:
- void reportBug(CheckerContext &C, const CallEvent &Call) const;
+ void reportBug(CheckerContext &C, const MemRegion *DerefRegion,
+ const CallEvent &Call) const;
+ void explainDereference(llvm::raw_ostream &OS, const MemRegion *DerefRegion,
+ const CallEvent &Call) const;
};
} // end of anonymous namespace
+// Define the inter-checker API.
+namespace clang {
+namespace ento {
+namespace smartptr {
+
+const BugType *getNullDereferenceBugType() { return NullDereferenceBugTypePtr; }
+
+} // namespace smartptr
+} // namespace ento
+} // namespace clang
+
void SmartPtrChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!smartptr::isStdSmartPtrCall(Call))
@@ -55,23 +72,34 @@ void SmartPtrChecker::checkPreCall(const CallEvent &Call,
OverloadedOperatorKind OOK = OC->getOverloadedOperator();
if (OOK == OO_Star || OOK == OO_Arrow) {
if (smartptr::isNullSmartPtr(State, ThisRegion))
- reportBug(C, Call);
+ reportBug(C, ThisRegion, Call);
}
}
-void SmartPtrChecker::reportBug(CheckerContext &C,
+void SmartPtrChecker::reportBug(CheckerContext &C, const MemRegion *DerefRegion,
const CallEvent &Call) const {
ExplodedNode *ErrNode = C.generateErrorNode();
if (!ErrNode)
return;
-
- auto R = std::make_unique<PathSensitiveBugReport>(
- NullDereferenceBugType, "Dereference of null smart pointer", ErrNode);
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ explainDereference(OS, DerefRegion, Call);
+ auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType,
+ OS.str(), ErrNode);
+ R->markInteresting(DerefRegion);
C.emitReport(std::move(R));
}
+void SmartPtrChecker::explainDereference(llvm::raw_ostream &OS,
+ const MemRegion *DerefRegion,
+ const CallEvent &Call) const {
+ OS << "Dereference of null smart pointer ";
+ DerefRegion->printPretty(OS);
+}
+
void ento::registerSmartPtrChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<SmartPtrChecker>();
+ SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>();
+ NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType;
}
bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index bcc7d4103c1c..6ee7bd9252b3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -15,24 +15,31 @@
#include "SmartPtr.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.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"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include <string>
using namespace clang;
using namespace ento;
namespace {
-class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
+class SmartPtrModeling
+ : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
+ check::LiveSymbols> {
- bool isNullAfterMoveMethod(const CallEvent &Call) const;
+ bool isBoolConversionMethod(const CallEvent &Call) const;
public:
// Whether the checker should model for null dereferences of smart pointers.
@@ -40,20 +47,35 @@ public:
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx, const CallEvent *Call) const;
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
private:
- ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
- const MemRegion *ThisValRegion) const;
void handleReset(const CallEvent &Call, CheckerContext &C) const;
void handleRelease(const CallEvent &Call, CheckerContext &C) const;
void handleSwap(const CallEvent &Call, CheckerContext &C) const;
+ void handleGet(const CallEvent &Call, CheckerContext &C) const;
+ bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
+ bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
+ const MemRegion *ThisRegion) const;
+ bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
+ const MemRegion *OtherSmartPtrRegion) const;
+ void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
using SmartPtrMethodHandlerFn =
void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
{{"reset"}, &SmartPtrModeling::handleReset},
{{"release"}, &SmartPtrModeling::handleRelease},
- {{"swap", 1}, &SmartPtrModeling::handleSwap}};
+ {{"swap", 1}, &SmartPtrModeling::handleSwap},
+ {{"get"}, &SmartPtrModeling::handleGet}};
};
} // end of anonymous namespace
@@ -81,13 +103,70 @@ bool isStdSmartPtrCall(const CallEvent &Call) {
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
- return InnerPointVal && InnerPointVal->isZeroConstant();
+ return InnerPointVal &&
+ !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
}
} // namespace smartptr
} // namespace ento
} // namespace clang
-bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
+// If a region is removed all of the subregions need to be removed too.
+static TrackedRegionMapTy
+removeTrackedSubregions(TrackedRegionMapTy RegionMap,
+ TrackedRegionMapTy::Factory &RegionMapFactory,
+ const MemRegion *Region) {
+ if (!Region)
+ return RegionMap;
+ for (const auto &E : RegionMap) {
+ if (E.first->isSubRegionOf(Region))
+ RegionMap = RegionMapFactory.remove(RegionMap, E.first);
+ }
+ return RegionMap;
+}
+
+static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
+ const MemRegion *Region,
+ const SVal *RegionInnerPointerVal) {
+ if (RegionInnerPointerVal) {
+ State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
+ } else {
+ State = State->remove<TrackedRegionMap>(Region);
+ }
+ return State;
+}
+
+// Helper method to get the inner pointer type of specialized smart pointer
+// Returns empty type if not found valid inner pointer type.
+static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
+ const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
+ if (!MethodDecl || !MethodDecl->getParent())
+ return {};
+
+ const auto *RecordDecl = MethodDecl->getParent();
+ if (!RecordDecl || !RecordDecl->isInStdNamespace())
+ return {};
+
+ const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
+ if (!TSD)
+ return {};
+
+ auto TemplateArgs = TSD->getTemplateArgs().asArray();
+ if (TemplateArgs.size() == 0)
+ return {};
+ auto InnerValueType = TemplateArgs[0].getAsType();
+ return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
+}
+
+// Helper method to pretty print region and avoid extra spacing.
+static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
+ const MemRegion *Region) {
+ if (Region->canPrintPretty()) {
+ OS << " ";
+ Region->printPretty(OS);
+ }
+}
+
+bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
// TODO: Update CallDescription to support anonymous calls?
// TODO: Handle other methods, such as .get() or .release().
// But once we do, we'd need a visitor to explain null dereferences
@@ -98,43 +177,93 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
bool SmartPtrModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
-
+ ProgramStateRef State = C.getState();
if (!smartptr::isStdSmartPtrCall(Call))
return false;
- if (isNullAfterMoveMethod(Call)) {
- ProgramStateRef State = C.getState();
+ if (isBoolConversionMethod(Call)) {
const MemRegion *ThisR =
cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
- if (!move::isMovedFrom(State, ThisR)) {
- // TODO: Model this case as well. At least, avoid invalidation of globals.
- return false;
+ if (ModelSmartPtrDereference) {
+ // The check for the region is moved is duplicated in handleBoolOperation
+ // method.
+ // FIXME: Once we model std::move for smart pointers clean up this and use
+ // that modeling.
+ handleBoolConversion(Call, C);
+ return true;
+ } else {
+ if (!move::isMovedFrom(State, ThisR)) {
+ // TODO: Model this case as well. At least, avoid invalidation of
+ // globals.
+ return false;
+ }
+
+ // TODO: Add a note to bug reports describing this decision.
+ C.addTransition(State->BindExpr(
+ Call.getOriginExpr(), C.getLocationContext(),
+ C.getSValBuilder().makeZeroVal(Call.getResultType())));
+
+ return true;
}
-
- // TODO: Add a note to bug reports describing this decision.
- C.addTransition(
- State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- C.getSValBuilder().makeZeroVal(Call.getResultType())));
- return true;
}
if (!ModelSmartPtrDereference)
return false;
if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
- if (CC->getDecl()->isCopyOrMoveConstructor())
+ if (CC->getDecl()->isCopyConstructor())
return false;
- const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
- if (!ThisValRegion)
+ const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
return false;
- auto State = updateTrackedRegion(Call, C, ThisValRegion);
- C.addTransition(State);
+ if (CC->getDecl()->isMoveConstructor())
+ return handleMoveCtr(Call, C, ThisRegion);
+
+ if (Call.getNumArgs() == 0) {
+ auto NullVal = C.getSValBuilder().makeNull();
+ State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
+
+ C.addTransition(
+ State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+ OS << "Default constructed smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " is null";
+ }));
+ } else {
+ const auto *TrackingExpr = Call.getArgExpr(0);
+ assert(TrackingExpr->getType()->isPointerType() &&
+ "Adding a non pointer value to TrackedRegionMap");
+ auto ArgVal = Call.getArgSVal(0);
+ State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
+
+ C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
+ ArgVal](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+ bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ if (ArgVal.isZeroConstant())
+ OS << " is constructed using a null value";
+ else
+ OS << " is constructed";
+ }));
+ }
return true;
}
+ if (handleAssignOp(Call, C))
+ return true;
+
const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
if (!Handler)
return false;
@@ -158,66 +287,351 @@ void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
C.addTransition(State);
}
+void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
+
+ if (!RS.isEmpty()) {
+ Out << Sep << "Smart ptr regions :" << NL;
+ for (auto I : RS) {
+ I.first->dumpToStream(Out);
+ if (smartptr::isNullSmartPtr(State, I.first))
+ Out << ": Null";
+ else
+ Out << ": Non Null";
+ Out << NL;
+ }
+ }
+}
+
+ProgramStateRef SmartPtrModeling::checkRegionChanges(
+ ProgramStateRef State, const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
+ const CallEvent *Call) const {
+ TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
+ TrackedRegionMapTy::Factory &RegionMapFactory =
+ State->get_context<TrackedRegionMap>();
+ for (const auto *Region : Regions)
+ RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
+ Region->getBaseRegion());
+ return State->set<TrackedRegionMap>(RegionMap);
+}
+
+void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
+ // Marking tracked symbols alive
+ TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
+ for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
+ SVal Val = I->second;
+ for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
+ SR.markLive(*si);
+ }
+ }
+}
+
void SmartPtrModeling::handleReset(const CallEvent &Call,
CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC)
return;
- const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
- if (!ThisValRegion)
+ const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
return;
- auto State = updateTrackedRegion(Call, C, ThisValRegion);
- C.addTransition(State);
- // TODO: Make sure to ivalidate the the region in the Store if we don't have
+
+ assert(Call.getArgExpr(0)->getType()->isPointerType() &&
+ "Adding a non pointer value to TrackedRegionMap");
+ State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
+ const auto *TrackingExpr = Call.getArgExpr(0);
+ C.addTransition(
+ State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+ bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " reset using a null value";
+ }));
+ // TODO: Make sure to ivalidate the region in the Store if we don't have
// time to model all methods.
}
void SmartPtrModeling::handleRelease(const CallEvent &Call,
CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC)
return;
- const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
- if (!ThisValRegion)
+ const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
return;
- auto State = updateTrackedRegion(Call, C, ThisValRegion);
+ const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
- const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
if (InnerPointVal) {
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
*InnerPointVal);
}
- C.addTransition(State);
+
+ auto ValueToUpdate = C.getSValBuilder().makeNull();
+ State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
+
+ C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " is released and set to null";
+ }));
// TODO: Add support to enable MallocChecker to start tracking the raw
// pointer.
}
void SmartPtrModeling::handleSwap(const CallEvent &Call,
CheckerContext &C) const {
- // TODO: Add support to handle swap method.
+ // To model unique_ptr::swap() method.
+ const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
+ if (!IC)
+ return;
+
+ const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
+ return;
+
+ const auto *ArgRegion = Call.getArgSVal(0).getAsRegion();
+ if (!ArgRegion)
+ return;
+
+ auto State = C.getState();
+ const auto *ThisRegionInnerPointerVal =
+ State->get<TrackedRegionMap>(ThisRegion);
+ const auto *ArgRegionInnerPointerVal =
+ State->get<TrackedRegionMap>(ArgRegion);
+
+ // Swap the tracked region values.
+ State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
+ State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);
+
+ C.addTransition(
+ State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+ BR.markInteresting(ArgRegion);
+ OS << "Swapped null smart pointer";
+ checkAndPrettyPrintRegion(OS, ArgRegion);
+ OS << " with smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ }));
}
-ProgramStateRef
-SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
- const MemRegion *ThisValRegion) const {
- // TODO: Refactor and clean up handling too many things.
+void SmartPtrModeling::handleGet(const CallEvent &Call,
+ CheckerContext &C) const {
ProgramStateRef State = C.getState();
- auto NumArgs = Call.getNumArgs();
-
- if (NumArgs == 0) {
- auto NullSVal = C.getSValBuilder().makeNull();
- State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
- } else if (NumArgs == 1) {
- auto ArgVal = Call.getArgSVal(0);
- assert(Call.getArgExpr(0)->getType()->isPointerType() &&
- "Adding a non pointer value to TrackedRegionMap");
- State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
+ const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
+ if (!IC)
+ return;
+
+ const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
+ return;
+
+ SVal InnerPointerVal;
+ if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
+ InnerPointerVal = *InnerValPtr;
+ } else {
+ const auto *CallExpr = Call.getOriginExpr();
+ InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
+ CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
+ State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
}
- return State;
+ State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ InnerPointerVal);
+ // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
+ C.addTransition(State);
+}
+
+bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
+ if (!OC)
+ return false;
+ OverloadedOperatorKind OOK = OC->getOverloadedOperator();
+ if (OOK != OO_Equal)
+ return false;
+ const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
+ return false;
+
+ const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
+ // In case of 'nullptr' or '0' assigned
+ if (!OtherSmartPtrRegion) {
+ bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
+ if (!AssignedNull)
+ return false;
+ auto NullVal = C.getSValBuilder().makeNull();
+ State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
+ C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(ThisRegion))
+ return;
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " is assigned to null";
+ }));
+ return true;
+ }
+
+ return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
+}
+
+bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
+ const MemRegion *ThisRegion) const {
+ const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
+ if (!OtherSmartPtrRegion)
+ return false;
+
+ return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
+}
+
+bool SmartPtrModeling::updateMovedSmartPointers(
+ CheckerContext &C, const MemRegion *ThisRegion,
+ const MemRegion *OtherSmartPtrRegion) const {
+ ProgramStateRef State = C.getState();
+ const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
+ if (OtherInnerPtr) {
+ State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
+ auto NullVal = C.getSValBuilder().makeNull();
+ State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
+ bool IsArgValNull = OtherInnerPtr->isZeroConstant();
+
+ C.addTransition(
+ State,
+ C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
+ return;
+ if (BR.isInteresting(OtherSmartPtrRegion)) {
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
+ OS << " is null after being moved to";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ }
+ if (BR.isInteresting(ThisRegion) && IsArgValNull) {
+ OS << "A null pointer value is moved to";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ BR.markInteresting(OtherSmartPtrRegion);
+ }
+ }));
+ return true;
+ } else {
+ // In case we dont know anything about value we are moving from
+ // remove the entry from map for which smart pointer got moved to.
+ auto NullVal = C.getSValBuilder().makeNull();
+ State = State->remove<TrackedRegionMap>(ThisRegion);
+ State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
+ C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
+ ThisRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
+ !BR.isInteresting(OtherSmartPtrRegion))
+ return;
+ OS << "Smart pointer";
+ checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
+ OS << " is null after; previous value moved to";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ }));
+ return true;
+ }
+ return false;
+}
+
+void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
+ CheckerContext &C) const {
+ // To model unique_ptr::operator bool
+ ProgramStateRef State = C.getState();
+ const Expr *CallExpr = Call.getOriginExpr();
+ const MemRegion *ThisRegion =
+ cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
+
+ SVal InnerPointerVal;
+ if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
+ InnerPointerVal = *InnerValPtr;
+ } else {
+ // In case of inner pointer SVal is not available we create
+ // conjureSymbolVal for inner pointer value.
+ auto InnerPointerType = getInnerPointerType(Call, C);
+ if (InnerPointerType.isNull())
+ return;
+
+ const LocationContext *LC = C.getLocationContext();
+ InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
+ CallExpr, LC, InnerPointerType, C.blockCount());
+ State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
+ }
+
+ if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
+ State = State->BindExpr(CallExpr, C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(false));
+
+ C.addTransition(State);
+ return;
+ } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
+ State = State->BindExpr(CallExpr, C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(true));
+
+ C.addTransition(State);
+ return;
+ } else if (move::isMovedFrom(State, ThisRegion)) {
+ C.addTransition(
+ State->BindExpr(CallExpr, C.getLocationContext(),
+ C.getSValBuilder().makeZeroVal(Call.getResultType())));
+ return;
+ } else {
+ ProgramStateRef NotNullState, NullState;
+ std::tie(NotNullState, NullState) =
+ State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
+
+ auto NullVal = C.getSValBuilder().makeNull();
+ // Explicitly tracking the region as null.
+ NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
+
+ NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(false));
+ C.addTransition(NullState, C.getNoteTag(
+ [ThisRegion](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &OS) {
+ OS << "Assuming smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " is null";
+ },
+ /*IsPrunable=*/true));
+ NotNullState =
+ NotNullState->BindExpr(CallExpr, C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(true));
+ C.addTransition(
+ NotNullState,
+ C.getNoteTag(
+ [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
+ OS << "Assuming smart pointer";
+ checkAndPrettyPrintRegion(OS, ThisRegion);
+ OS << " is non-null";
+ },
+ /*IsPrunable=*/true));
+ return;
+ }
}
void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 8b575f4f4759..d1c366a94fac 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -40,12 +40,12 @@
//
// The following standard C functions are currently supported:
//
-// fgetc getline isdigit isupper
+// fgetc getline isdigit isupper toascii
// fread isalnum isgraph isxdigit
// fwrite isalpha islower read
// getc isascii isprint write
-// getchar isblank ispunct
-// getdelim iscntrl isspace
+// getchar isblank ispunct toupper
+// getdelim iscntrl isspace tolower
//
//===----------------------------------------------------------------------===//
@@ -126,6 +126,8 @@ class StdLibraryFunctionsChecker
}
ArgNo getArgNo() const { return ArgN; }
+ virtual StringRef getName() const = 0;
+
protected:
ArgNo ArgN; // Argument to which we apply the constraint.
@@ -138,18 +140,25 @@ class StdLibraryFunctionsChecker
/// Given a range, should the argument stay inside or outside this range?
enum RangeKind { OutOfRange, WithinRange };
- /// Encapsulates a single range on a single symbol within a branch.
+ /// Encapsulates a range on a single symbol.
class RangeConstraint : public ValueConstraint {
- RangeKind Kind; // Kind of range definition.
- IntRangeVector Args; // Polymorphic arguments.
+ RangeKind Kind;
+ // A range is formed as a set of intervals (sub-ranges).
+ // E.g. {['A', 'Z'], ['a', 'z']}
+ //
+ // The default constructed RangeConstraint has an empty range set, applying
+ // such constraint does not involve any assumptions, thus the State remains
+ // unchanged. This is meaningful, if the range is dependent on a looked up
+ // type (e.g. [0, Socklen_tMax]). If the type is not found, then the range
+ // is default initialized to be empty.
+ IntRangeVector Ranges;
public:
- RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args)
- : ValueConstraint(ArgN), Kind(Kind), Args(Args) {}
+ StringRef getName() const override { return "Range"; }
+ RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges)
+ : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {}
- const IntRangeVector &getRanges() const {
- return Args;
- }
+ const IntRangeVector &getRanges() const { return Ranges; }
private:
ProgramStateRef applyAsOutOfRange(ProgramStateRef State,
@@ -158,6 +167,7 @@ class StdLibraryFunctionsChecker
ProgramStateRef applyAsWithinRange(ProgramStateRef State,
const CallEvent &Call,
const Summary &Summary) const;
+
public:
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
@@ -198,6 +208,7 @@ class StdLibraryFunctionsChecker
ArgNo OtherArgN;
public:
+ virtual StringRef getName() const override { return "Comparison"; };
ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode,
ArgNo OtherArgN)
: ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {}
@@ -214,6 +225,7 @@ class StdLibraryFunctionsChecker
bool CannotBeNull = true;
public:
+ StringRef getName() const override { return "NonNull"; }
ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
const Summary &Summary,
CheckerContext &C) const override {
@@ -242,15 +254,21 @@ class StdLibraryFunctionsChecker
}
};
- // Represents a buffer argument with an additional size argument.
- // E.g. the first two arguments here:
+ // Represents a buffer argument with an additional size constraint. The
+ // constraint may be a concrete value, or a symbolic value in an argument.
+ // Example 1. Concrete value as the minimum buffer size.
+ // char *asctime_r(const struct tm *restrict tm, char *restrict buf);
+ // // `buf` size must be at least 26 bytes according the POSIX standard.
+ // Example 2. Argument as a buffer size.
// ctime_s(char *buffer, rsize_t bufsz, const time_t *time);
- // Another example:
+ // Example 3. The size is computed as a multiplication of other args.
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// // Here, ptr is the buffer, and its minimum size is `size * nmemb`.
class BufferSizeConstraint : public ValueConstraint {
+ // The concrete value which is the minimum size for the buffer.
+ llvm::Optional<llvm::APSInt> ConcreteSize;
// The argument which holds the size of the buffer.
- ArgNo SizeArgN;
+ llvm::Optional<ArgNo> SizeArgN;
// The argument which is a multiplier to size. This is set in case of
// `fread` like functions where the size is computed as a multiplication of
// two arguments.
@@ -259,9 +277,11 @@ class StdLibraryFunctionsChecker
BinaryOperator::Opcode Op = BO_LE;
public:
+ StringRef getName() const override { return "BufferSize"; }
+ BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize)
+ : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {}
BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize)
: ValueConstraint(Buffer), SizeArgN(BufSize) {}
-
BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier)
: ValueConstraint(Buffer), SizeArgN(BufSize),
SizeMultiplierArgN(BufSizeMultiplier) {}
@@ -272,14 +292,27 @@ class StdLibraryFunctionsChecker
SValBuilder &SvalBuilder = C.getSValBuilder();
// The buffer argument.
SVal BufV = getArgSVal(Call, getArgNo());
- // The size argument.
- SVal SizeV = getArgSVal(Call, SizeArgN);
- // Multiply with another argument if given.
- if (SizeMultiplierArgN) {
- SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN);
- SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV,
- Summary.getArgType(SizeArgN));
- }
+
+ // Get the size constraint.
+ const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() {
+ if (ConcreteSize) {
+ return SVal(SvalBuilder.makeIntVal(*ConcreteSize));
+ } else if (SizeArgN) {
+ // The size argument.
+ SVal SizeV = getArgSVal(Call, *SizeArgN);
+ // Multiply with another argument if given.
+ if (SizeMultiplierArgN) {
+ SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN);
+ SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV,
+ Summary.getArgType(*SizeArgN));
+ }
+ return SizeV;
+ } else {
+ llvm_unreachable("The constraint must be either a concrete value or "
+ "encoded in an arguement.");
+ }
+ }();
+
// The dynamic size of the buffer argument, got from the analyzer engine.
SVal BufDynSize = getDynamicSizeWithOffset(State, BufV);
@@ -302,12 +335,20 @@ class StdLibraryFunctionsChecker
Tmp.Op = BinaryOperator::negateComparisonOp(Op);
return std::make_shared<BufferSizeConstraint>(Tmp);
}
+
+ bool checkSpecificValidity(const FunctionDecl *FD) const override {
+ const bool ValidArg = getArgType(FD, ArgN)->isPointerType();
+ assert(ValidArg &&
+ "This constraint should be applied only on a pointer type");
+ return ValidArg;
+ }
};
/// The complete list of constraints that defines a single branch.
typedef std::vector<ValueConstraintPtr> ConstraintSet;
- using ArgTypes = std::vector<QualType>;
+ using ArgTypes = std::vector<Optional<QualType>>;
+ using RetType = Optional<QualType>;
// A placeholder type, we use it whenever we do not care about the concrete
// type in a Signature.
@@ -317,16 +358,37 @@ class StdLibraryFunctionsChecker
// The signature of a function we want to describe with a summary. This is a
// concessive signature, meaning there may be irrelevant types in the
// signature which we do not check against a function with concrete types.
- struct Signature {
- const ArgTypes ArgTys;
- const QualType RetTy;
- Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) {
- assertRetTypeSuitableForSignature(RetTy);
- for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
- QualType ArgTy = ArgTys[I];
- assertArgTypeSuitableForSignature(ArgTy);
+ // All types in the spec need to be canonical.
+ class Signature {
+ using ArgQualTypes = std::vector<QualType>;
+ ArgQualTypes ArgTys;
+ QualType RetTy;
+ // True if any component type is not found by lookup.
+ bool Invalid = false;
+
+ public:
+ // Construct a signature from optional types. If any of the optional types
+ // are not set then the signature will be invalid.
+ Signature(ArgTypes ArgTys, RetType RetTy) {
+ for (Optional<QualType> Arg : ArgTys) {
+ if (!Arg) {
+ Invalid = true;
+ return;
+ } else {
+ assertArgTypeSuitableForSignature(*Arg);
+ this->ArgTys.push_back(*Arg);
+ }
+ }
+ if (!RetTy) {
+ Invalid = true;
+ return;
+ } else {
+ assertRetTypeSuitableForSignature(*RetTy);
+ this->RetTy = *RetTy;
}
}
+
+ bool isInvalid() const { return Invalid; }
bool matches(const FunctionDecl *FD) const;
private:
@@ -380,7 +442,6 @@ class StdLibraryFunctionsChecker
/// rules for the given parameter's type, those rules are checked once the
/// signature is matched.
class Summary {
- const Signature Sign;
const InvalidationKind InvalidationKd;
Cases CaseConstraints;
ConstraintSet ArgConstraints;
@@ -390,14 +451,19 @@ class StdLibraryFunctionsChecker
const FunctionDecl *FD = nullptr;
public:
- Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd)
- : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {}
+ Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {}
- Summary &Case(ConstraintSet&& CS) {
+ Summary &Case(ConstraintSet &&CS) {
CaseConstraints.push_back(std::move(CS));
return *this;
}
+ Summary &Case(const ConstraintSet &CS) {
+ CaseConstraints.push_back(CS);
+ return *this;
+ }
Summary &ArgConstraint(ValueConstraintPtr VC) {
+ assert(VC->getArgNo() != Ret &&
+ "Arg constraint should not refer to the return value");
ArgConstraints.push_back(VC);
return *this;
}
@@ -412,7 +478,7 @@ class StdLibraryFunctionsChecker
// Returns true if the summary should be applied to the given function.
// And if yes then store the function declaration.
- bool matchesAndSet(const FunctionDecl *FD) {
+ bool matchesAndSet(const Signature &Sign, const FunctionDecl *FD) {
bool Result = Sign.matches(FD) && validateByConstraints(FD);
if (Result) {
assert(!this->FD && "FD must not be set more than once");
@@ -472,17 +538,24 @@ private:
void initFunctionSummaries(CheckerContext &C) const;
void reportBug(const CallEvent &Call, ExplodedNode *N,
- CheckerContext &C) const {
+ const ValueConstraint *VC, CheckerContext &C) const {
if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker])
return;
- // TODO Add detailed diagnostic.
- StringRef Msg = "Function argument constraint is not satisfied";
+ // TODO Add more detailed diagnostic.
+ std::string Msg =
+ (Twine("Function argument constraint is not satisfied, constraint: ") +
+ VC->getName().data() + ", ArgN: " + Twine(VC->getArgNo()))
+ .str();
if (!BT_InvalidArg)
BT_InvalidArg = std::make_unique<BugType>(
CheckNames[CK_StdCLibraryFunctionArgsChecker],
"Unsatisfied argument constraints", categories::LogicError);
auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N);
- bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(VC->getArgNo()), *R);
+
+ // Highlight the range of the argument that was violated.
+ R->addRange(Call.getArgSourceRange(VC->getArgNo()));
+
C.emitReport(std::move(R));
}
};
@@ -495,6 +568,8 @@ const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret =
ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange(
ProgramStateRef State, const CallEvent &Call,
const Summary &Summary) const {
+ if (Ranges.empty())
+ return State;
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
@@ -522,6 +597,8 @@ ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange(
ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange(
ProgramStateRef State, const CallEvent &Call,
const Summary &Summary) const {
+ if (Ranges.empty())
+ return State;
ProgramStateManager &Mgr = State->getStateManager();
SValBuilder &SVB = Mgr.getSValBuilder();
@@ -615,7 +692,7 @@ void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call,
// The argument constraint is not satisfied.
if (FailureSt && !SuccessSt) {
if (ExplodedNode *N = C.generateErrorNode(NewState))
- reportBug(Call, N, C);
+ reportBug(Call, N, Constraint.get(), C);
break;
} else {
// We will apply the constraint even if we cannot reason about the
@@ -665,7 +742,7 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
case EvalCallAsPure: {
ProgramStateRef State = C.getState();
const LocationContext *LC = C.getLocationContext();
- const auto *CE = cast_or_null<CallExpr>(Call.getOriginExpr());
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
SVal V = C.getSValBuilder().conjureSymbolVal(
CE, LC, CE->getType().getCanonicalType(), C.blockCount());
State = State->BindExpr(CE, LC, V);
@@ -682,21 +759,39 @@ bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
bool StdLibraryFunctionsChecker::Signature::matches(
const FunctionDecl *FD) const {
- // Check number of arguments:
+ assert(!isInvalid());
+ // Check the number of arguments.
if (FD->param_size() != ArgTys.size())
return false;
- // Check return type.
- if (!isIrrelevant(RetTy))
- if (RetTy != FD->getReturnType().getCanonicalType())
+ // The "restrict" keyword is illegal in C++, however, many libc
+ // implementations use the "__restrict" compiler intrinsic in functions
+ // prototypes. The "__restrict" keyword qualifies a type as a restricted type
+ // even in C++.
+ // In case of any non-C99 languages, we don't want to match based on the
+ // restrict qualifier because we cannot know if the given libc implementation
+ // qualifies the paramter type or not.
+ auto RemoveRestrict = [&FD](QualType T) {
+ if (!FD->getASTContext().getLangOpts().C99)
+ T.removeLocalRestrict();
+ return T;
+ };
+
+ // Check the return type.
+ if (!isIrrelevant(RetTy)) {
+ QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType());
+ if (RetTy != FDRetTy)
return false;
+ }
- // Check argument types.
+ // Check the argument types.
for (size_t I = 0, E = ArgTys.size(); I != E; ++I) {
QualType ArgTy = ArgTys[I];
if (isIrrelevant(ArgTy))
continue;
- if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType())
+ QualType FDArgTy =
+ RemoveRestrict(FD->getParamDecl(I)->getType().getCanonicalType());
+ if (ArgTy != FDArgTy)
return false;
}
@@ -726,32 +821,6 @@ StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call,
return findFunctionSummary(FD, C);
}
-static llvm::Optional<QualType> lookupType(StringRef Name,
- const ASTContext &ACtx) {
- IdentifierInfo &II = ACtx.Idents.get(Name);
- auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
- if (LookupRes.size() == 0)
- return None;
-
- // Prioritze typedef declarations.
- // This is needed in case of C struct typedefs. E.g.:
- // typedef struct FILE FILE;
- // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and
- // we have a TypedefDecl with the name 'FILE'.
- for (Decl *D : LookupRes)
- if (auto *TD = dyn_cast<TypedefNameDecl>(D))
- return ACtx.getTypeDeclType(TD).getCanonicalType();
-
- // Find the first TypeDecl.
- // There maybe cases when a function has the same name as a struct.
- // E.g. in POSIX: `struct stat` and the function `stat()`:
- // int stat(const char *restrict path, struct stat *restrict buf);
- for (Decl *D : LookupRes)
- if (auto *TD = dyn_cast<TypeDecl>(D))
- return ACtx.getTypeDeclType(TD).getCanonicalType();
- return None;
-}
-
void StdLibraryFunctionsChecker::initFunctionSummaries(
CheckerContext &C) const {
if (!FunctionSummaryMap.empty())
@@ -761,6 +830,91 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
BasicValueFactory &BVF = SVB.getBasicValueFactory();
const ASTContext &ACtx = BVF.getContext();
+ // Helper class to lookup a type by its name.
+ class LookupType {
+ const ASTContext &ACtx;
+
+ public:
+ LookupType(const ASTContext &ACtx) : ACtx(ACtx) {}
+
+ // Find the type. If not found then the optional is not set.
+ llvm::Optional<QualType> operator()(StringRef Name) {
+ IdentifierInfo &II = ACtx.Idents.get(Name);
+ auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
+ if (LookupRes.size() == 0)
+ return None;
+
+ // Prioritze typedef declarations.
+ // This is needed in case of C struct typedefs. E.g.:
+ // typedef struct FILE FILE;
+ // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE'
+ // and we have a TypedefDecl with the name 'FILE'.
+ for (Decl *D : LookupRes)
+ if (auto *TD = dyn_cast<TypedefNameDecl>(D))
+ return ACtx.getTypeDeclType(TD).getCanonicalType();
+
+ // Find the first TypeDecl.
+ // There maybe cases when a function has the same name as a struct.
+ // E.g. in POSIX: `struct stat` and the function `stat()`:
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ for (Decl *D : LookupRes)
+ if (auto *TD = dyn_cast<TypeDecl>(D))
+ return ACtx.getTypeDeclType(TD).getCanonicalType();
+ return None;
+ }
+ } lookupTy(ACtx);
+
+ // Below are auxiliary classes to handle optional types that we get as a
+ // result of the lookup.
+ class GetRestrictTy {
+ const ASTContext &ACtx;
+
+ public:
+ GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {}
+ QualType operator()(QualType Ty) {
+ return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty;
+ }
+ Optional<QualType> operator()(Optional<QualType> Ty) {
+ if (Ty)
+ return operator()(*Ty);
+ return None;
+ }
+ } getRestrictTy(ACtx);
+ class GetPointerTy {
+ const ASTContext &ACtx;
+
+ public:
+ GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {}
+ QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); }
+ Optional<QualType> operator()(Optional<QualType> Ty) {
+ if (Ty)
+ return operator()(*Ty);
+ return None;
+ }
+ } getPointerTy(ACtx);
+ class {
+ public:
+ Optional<QualType> operator()(Optional<QualType> Ty) {
+ return Ty ? Optional<QualType>(Ty->withConst()) : None;
+ }
+ QualType operator()(QualType Ty) { return Ty.withConst(); }
+ } getConstTy;
+ class GetMaxValue {
+ BasicValueFactory &BVF;
+
+ public:
+ GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {}
+ Optional<RangeInt> operator()(QualType Ty) {
+ return BVF.getMaxValue(Ty).getLimitedValue();
+ }
+ Optional<RangeInt> operator()(Optional<QualType> Ty) {
+ if (Ty) {
+ return operator()(*Ty);
+ }
+ return None;
+ }
+ } getMaxValue(BVF);
+
// These types are useful for writing specifications quickly,
// New specifications should probably introduce more types.
// Some types are hard to obtain from the AST, eg. "ssize_t".
@@ -769,44 +923,36 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// or long long, so three summary variants would be enough).
// Of course, function variants are also useful for C++ overloads.
const QualType VoidTy = ACtx.VoidTy;
+ const QualType CharTy = ACtx.CharTy;
+ const QualType WCharTy = ACtx.WCharTy;
const QualType IntTy = ACtx.IntTy;
const QualType UnsignedIntTy = ACtx.UnsignedIntTy;
const QualType LongTy = ACtx.LongTy;
- const QualType LongLongTy = ACtx.LongLongTy;
const QualType SizeTy = ACtx.getSizeType();
- const QualType VoidPtrTy = ACtx.VoidPtrTy; // void *
- const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int *
+ const QualType VoidPtrTy = getPointerTy(VoidTy); // void *
+ const QualType IntPtrTy = getPointerTy(IntTy); // int *
const QualType UnsignedIntPtrTy =
- ACtx.getPointerType(UnsignedIntTy); // unsigned int *
- const QualType VoidPtrRestrictTy =
- ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict
- : VoidPtrTy;
+ getPointerTy(UnsignedIntTy); // unsigned int *
+ const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy);
const QualType ConstVoidPtrTy =
- ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void *
- const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char *
- const QualType CharPtrRestrictTy =
- ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict
- : CharPtrTy;
+ getPointerTy(getConstTy(VoidTy)); // const void *
+ const QualType CharPtrTy = getPointerTy(CharTy); // char *
+ const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy);
const QualType ConstCharPtrTy =
- ACtx.getPointerType(ACtx.CharTy.withConst()); // const char *
- const QualType ConstCharPtrRestrictTy =
- ACtx.getLangOpts().C99
- ? ACtx.getRestrictType(ConstCharPtrTy) // const char *restrict
- : ConstCharPtrTy;
- const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t *
+ getPointerTy(getConstTy(CharTy)); // const char *
+ const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy);
+ const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t *
const QualType ConstWchar_tPtrTy =
- ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t *
- const QualType ConstVoidPtrRestrictTy =
- ACtx.getLangOpts().C99
- ? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict
- : ConstVoidPtrTy;
+ getPointerTy(getConstTy(WCharTy)); // const wchar_t *
+ const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy);
+ const QualType SizePtrTy = getPointerTy(SizeTy);
+ const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy);
const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
const RangeInt UnsignedIntMax =
BVF.getMaxValue(UnsignedIntTy).getLimitedValue();
const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
- const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue();
// Set UCharRangeMax to min of int or uchar maximum value.
@@ -840,15 +986,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
// Add a summary to a FunctionDecl found by lookup. The lookup is performed
// by the given Name, and in the global scope. The summary will be attached
// to the found FunctionDecl only if the signatures match.
- void operator()(StringRef Name, Summary S) {
+ //
+ // Returns true if the summary has been added, false otherwise.
+ bool operator()(StringRef Name, Signature Sign, Summary Sum) {
+ if (Sign.isInvalid())
+ return false;
IdentifierInfo &II = ACtx.Idents.get(Name);
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
if (LookupRes.size() == 0)
- return;
+ return false;
for (Decl *D : LookupRes) {
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
- if (S.matchesAndSet(FD)) {
- auto Res = Map.insert({FD->getCanonicalDecl(), S});
+ if (Sum.matchesAndSet(Sign, FD)) {
+ auto Res = Map.insert({FD->getCanonicalDecl(), Sum});
assert(Res.second && "Function already has a summary set!");
(void)Res;
if (DisplayLoadedSummaries) {
@@ -856,44 +1006,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
FD->print(llvm::errs());
llvm::errs() << "\n";
}
- return;
+ return true;
}
}
}
+ return false;
}
- // Add several summaries for the given name.
- void operator()(StringRef Name, const std::vector<Summary> &Summaries) {
- for (const Summary &S : Summaries)
- operator()(Name, S);
+ // Add the same summary for different names with the Signature explicitly
+ // given.
+ void operator()(std::vector<StringRef> Names, Signature Sign, Summary Sum) {
+ for (StringRef Name : Names)
+ operator()(Name, Sign, Sum);
}
} addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries);
- // We are finally ready to define specifications for all supported functions.
- //
- // The signature needs to have the correct number of arguments.
- // However, we insert `Irrelevant' when the type is insignificant.
- //
- // Argument ranges should always cover all variants. If return value
- // is completely unknown, omit it from the respective range set.
- //
- // All types in the spec need to be canonical.
- //
- // Every item in the list of range sets represents a particular
- // execution path the analyzer would need to explore once
- // the call is modeled - a new program state is constructed
- // for every range set, and each range line in the range set
- // corresponds to a specific constraint within this state.
- //
- // Upon comparing to another argument, the other argument is casted
- // to the current argument's type. This avoids proper promotion but
- // seems useful. For example, read() receives size_t argument,
- // and its return value, which is of type ssize_t, cannot be greater
- // than this argument. If we made a promotion, and the size argument
- // is equal to, say, 10, then we'd impose a range of [0, 10] on the
- // return value, however the correct range is [-1, 10].
- //
- // Please update the list of functions in the header after editing!
-
// Below are helpers functions to create the summaries.
auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind,
IntRangeVector Ranges) {
@@ -910,9 +1036,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN);
}
} ReturnValueCondition;
- auto Range = [](RangeInt b, RangeInt e) {
- return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
- };
+ struct {
+ auto operator()(RangeInt b, RangeInt e) {
+ return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}};
+ }
+ auto operator()(RangeInt b, Optional<RangeInt> e) {
+ if (e)
+ return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}};
+ return IntRangeVector{};
+ }
+ auto operator()(std::pair<RangeInt, RangeInt> i0,
+ std::pair<RangeInt, Optional<RangeInt>> i1) {
+ if (i1.second)
+ return IntRangeVector{i0, {i1.first, *(i1.second)}};
+ return IntRangeVector{i0};
+ }
+ } Range;
auto SingleValue = [](RangeInt v) {
return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}};
};
@@ -921,60 +1060,28 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
return std::make_shared<NotNullConstraint>(ArgN);
};
- Optional<QualType> FileTy = lookupType("FILE", ACtx);
- Optional<QualType> FilePtrTy, FilePtrRestrictTy;
- if (FileTy) {
- // FILE *
- FilePtrTy = ACtx.getPointerType(*FileTy);
- // FILE *restrict
- FilePtrRestrictTy =
- ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy;
- }
+ Optional<QualType> FileTy = lookupTy("FILE");
+ Optional<QualType> FilePtrTy = getPointerTy(FileTy);
+ Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy);
- using RetType = QualType;
- // Templates for summaries that are reused by many functions.
- auto Getc = [&]() {
- return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
- .Case({ReturnValueCondition(WithinRange,
- {{EOFv, EOFv}, {0, UCharRangeMax}})});
- };
- auto Read = [&](RetType R, RangeInt Max) {
- return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R},
- NoEvalCall)
- .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- ReturnValueCondition(WithinRange, Range(-1, Max))});
- };
- auto Fread = [&]() {
- return Summary(
- ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy},
- RetType{SizeTy}, NoEvalCall)
- .Case({
- ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- })
- .ArgConstraint(NotNull(ArgNo(0)));
- };
- auto Fwrite = [&]() {
- return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy,
- *FilePtrRestrictTy},
- RetType{SizeTy}, NoEvalCall)
- .Case({
- ReturnValueCondition(LessThanOrEq, ArgNo(2)),
- })
- .ArgConstraint(NotNull(ArgNo(0)));
- };
- auto Getline = [&](RetType R, RangeInt Max) {
- return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R},
- NoEvalCall)
- .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})});
- };
+ // We are finally ready to define specifications for all supported functions.
+ //
+ // Argument ranges should always cover all variants. If return value
+ // is completely unknown, omit it from the respective range set.
+ //
+ // Every item in the list of range sets represents a particular
+ // execution path the analyzer would need to explore once
+ // the call is modeled - a new program state is constructed
+ // for every range set, and each range line in the range set
+ // corresponds to a specific constraint within this state.
// The isascii() family of functions.
// The behavior is undefined if the value of the argument is not
// representable as unsigned char or is not equal to EOF. See e.g. C99
// 7.4.1.2 The isalpha function (p: 181-182).
addToFunctionSummaryMap(
- "isalnum",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isalnum", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
// Boils down to isupper() or islower() or isdigit().
.Case({ArgumentCondition(0U, WithinRange,
{{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}),
@@ -991,8 +1098,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.ArgConstraint(ArgumentCondition(
0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
addToFunctionSummaryMap(
- "isalpha",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}),
ReturnValueCondition(OutOfRange, SingleValue(0))})
// The locale-specific range.
@@ -1002,43 +1109,43 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
{{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isascii",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(0, 127)),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isblank",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "iscntrl",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isdigit",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range('0', '9')),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isgraph",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isgraph", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(33, 126)),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "islower",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "islower", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
// Is certainly lowercase.
.Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')),
ReturnValueCondition(OutOfRange, SingleValue(0))})
@@ -1052,15 +1159,15 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
.Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isprint",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange, Range(32, 126)),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "ispunct",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(
0U, WithinRange,
{{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
@@ -1070,8 +1177,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
{{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isspace",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
// Space, '\f', '\n', '\r', '\t', '\v'.
.Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}),
ReturnValueCondition(OutOfRange, SingleValue(0))})
@@ -1081,8 +1188,8 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
{{9, 13}, {' ', ' '}, {128, UCharRangeMax}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isupper",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
// Is certainly uppercase.
.Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')),
ReturnValueCondition(OutOfRange, SingleValue(0))})
@@ -1093,650 +1200,1290 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
{{'A', 'Z'}, {128, UCharRangeMax}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
addToFunctionSummaryMap(
- "isxdigit",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.Case({ArgumentCondition(0U, WithinRange,
{{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
ReturnValueCondition(OutOfRange, SingleValue(0))})
.Case({ArgumentCondition(0U, OutOfRange,
{{'0', '9'}, {'A', 'F'}, {'a', 'f'}}),
ReturnValueCondition(WithinRange, SingleValue(0))}));
+ addToFunctionSummaryMap(
+ "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ addToFunctionSummaryMap(
+ "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
+ addToFunctionSummaryMap(
+ "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(ArgumentCondition(
+ 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})));
// The getc() family of functions that returns either a char or an EOF.
- if (FilePtrTy) {
- addToFunctionSummaryMap("getc", Getc());
- addToFunctionSummaryMap("fgetc", Getc());
- }
addToFunctionSummaryMap(
- "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall)
- .Case({ReturnValueCondition(
- WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})}));
+ {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}})}));
+ addToFunctionSummaryMap(
+ "getchar", Signature(ArgTypes{}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange,
+ {{EOFv, EOFv}, {0, UCharRangeMax}})}));
// read()-like functions that never return more than buffer size.
- if (FilePtrRestrictTy) {
- addToFunctionSummaryMap("fread", Fread());
- addToFunctionSummaryMap("fwrite", Fwrite());
- }
+ auto FreadSummary =
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(0, SizeMax))})
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(3)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
+ /*BufSizeMultiplier=*/ArgNo(2)));
+
+ // size_t fread(void *restrict ptr, size_t size, size_t nitems,
+ // FILE *restrict stream);
+ addToFunctionSummaryMap(
+ "fread",
+ Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy},
+ RetType{SizeTy}),
+ FreadSummary);
+ // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems,
+ // FILE *restrict stream);
+ addToFunctionSummaryMap("fwrite",
+ Signature(ArgTypes{ConstVoidPtrRestrictTy, SizeTy,
+ SizeTy, FilePtrRestrictTy},
+ RetType{SizeTy}),
+ FreadSummary);
+
+ Optional<QualType> Ssize_tTy = lookupTy("ssize_t");
+ Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy);
+
+ auto ReadSummary =
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))});
- // We are not sure how ssize_t is defined on every platform, so we
- // provide three variants that should cover common cases.
// FIXME these are actually defined by POSIX and not by the C standard, we
// should handle them together with the rest of the POSIX functions.
- addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax),
- Read(LongLongTy, LongLongMax)});
- addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax),
- Read(LongLongTy, LongLongMax)});
+ // ssize_t read(int fildes, void *buf, size_t nbyte);
+ addToFunctionSummaryMap(
+ "read", Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}),
+ ReadSummary);
+ // ssize_t write(int fildes, const void *buf, size_t nbyte);
+ addToFunctionSummaryMap(
+ "write",
+ Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}),
+ ReadSummary);
+
+ auto GetLineSummary =
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange,
+ Range({-1, -1}, {1, Ssize_tMax}))});
+
+ QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy));
// getline()-like functions either fail or read at least the delimiter.
// FIXME these are actually defined by POSIX and not by the C standard, we
// should handle them together with the rest of the POSIX functions.
- addToFunctionSummaryMap("getline",
- {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
- Getline(LongLongTy, LongLongMax)});
- addToFunctionSummaryMap("getdelim",
- {Getline(IntTy, IntMax), Getline(LongTy, LongMax),
- Getline(LongLongTy, LongLongMax)});
+ // ssize_t getline(char **restrict lineptr, size_t *restrict n,
+ // FILE *restrict stream);
+ addToFunctionSummaryMap(
+ "getline",
+ Signature(
+ ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy},
+ RetType{Ssize_tTy}),
+ GetLineSummary);
+ // ssize_t getdelim(char **restrict lineptr, size_t *restrict n,
+ // int delimiter, FILE *restrict stream);
+ addToFunctionSummaryMap(
+ "getdelim",
+ Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy,
+ FilePtrRestrictTy},
+ RetType{Ssize_tTy}),
+ GetLineSummary);
if (ModelPOSIX) {
// long a64l(const char *str64);
addToFunctionSummaryMap(
- "a64l", Summary(ArgTypes{ConstCharPtrTy}, RetType{LongTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ "a64l", Signature(ArgTypes{ConstCharPtrTy}, RetType{LongTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// char *l64a(long value);
- addToFunctionSummaryMap(
- "l64a", Summary(ArgTypes{LongTy}, RetType{CharPtrTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, LongMax))));
+ addToFunctionSummaryMap("l64a",
+ Signature(ArgTypes{LongTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, LongMax))));
+
+ const auto ReturnsZeroOrMinusOne =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))};
+ const auto ReturnsFileDescriptor =
+ ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))};
// int access(const char *pathname, int amode);
- addToFunctionSummaryMap("access", Summary(ArgTypes{ConstCharPtrTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int faccessat(int dirfd, const char *pathname, int mode, int flags);
addToFunctionSummaryMap(
- "faccessat", Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(1))));
+ "faccessat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
// int dup(int fildes);
- addToFunctionSummaryMap(
- "dup", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("dup", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
// int dup2(int fildes1, int filedes2);
addToFunctionSummaryMap(
- "dup2",
- Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall)
+ "dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(
ArgumentCondition(1, WithinRange, Range(0, IntMax))));
// int fdatasync(int fildes);
- addToFunctionSummaryMap(
- "fdatasync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax))));
+ addToFunctionSummaryMap("fdatasync",
+ Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
// int fnmatch(const char *pattern, const char *string, int flags);
addToFunctionSummaryMap(
- "fnmatch", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy},
- RetType{IntTy}, EvalCallAsPure)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ "fnmatch",
+ Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
// int fsync(int fildes);
- addToFunctionSummaryMap(
- "fsync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Off_tTy = lookupType("off_t", ACtx);
+ Optional<QualType> Off_tTy = lookupTy("off_t");
- if (Off_tTy)
- // int truncate(const char *path, off_t length);
- addToFunctionSummaryMap("truncate",
- Summary(ArgTypes{ConstCharPtrTy, *Off_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int truncate(const char *path, off_t length);
+ addToFunctionSummaryMap(
+ "truncate",
+ Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int symlink(const char *oldpath, const char *newpath);
- addToFunctionSummaryMap("symlink",
- Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ addToFunctionSummaryMap(
+ "symlink",
+ Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
// int symlinkat(const char *oldpath, int newdirfd, const char *newpath);
addToFunctionSummaryMap(
"symlinkat",
- Summary(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy},
- NoEvalCall)
+ Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(2))));
- if (Off_tTy)
- // int lockf(int fd, int cmd, off_t len);
- addToFunctionSummaryMap(
- "lockf",
- Summary(ArgTypes{IntTy, IntTy, *Off_tTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ // int lockf(int fd, int cmd, off_t len);
+ addToFunctionSummaryMap(
+ "lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Mode_tTy = lookupType("mode_t", ACtx);
+ Optional<QualType> Mode_tTy = lookupTy("mode_t");
- if (Mode_tTy)
- // int creat(const char *pathname, mode_t mode);
- addToFunctionSummaryMap("creat",
- Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int creat(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap(
+ "creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(NotNull(ArgNo(0))));
// unsigned int sleep(unsigned int seconds);
addToFunctionSummaryMap(
- "sleep",
- Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ "sleep", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}),
+ Summary(NoEvalCall)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
- Optional<QualType> DirTy = lookupType("DIR", ACtx);
- Optional<QualType> DirPtrTy;
- if (DirTy)
- DirPtrTy = ACtx.getPointerType(*DirTy);
+ Optional<QualType> DirTy = lookupTy("DIR");
+ Optional<QualType> DirPtrTy = getPointerTy(DirTy);
- if (DirPtrTy)
- // int dirfd(DIR *dirp);
- addToFunctionSummaryMap(
- "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int dirfd(DIR *dirp);
+ addToFunctionSummaryMap("dirfd",
+ Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(NotNull(ArgNo(0))));
// unsigned int alarm(unsigned int seconds);
addToFunctionSummaryMap(
- "alarm",
- Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall)
+ "alarm", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}),
+ Summary(NoEvalCall)
.ArgConstraint(
ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax))));
- if (DirPtrTy)
- // int closedir(DIR *dir);
- addToFunctionSummaryMap(
- "closedir", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int closedir(DIR *dir);
+ addToFunctionSummaryMap("closedir",
+ Signature(ArgTypes{DirPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// char *strdup(const char *s);
- addToFunctionSummaryMap("strdup", Summary(ArgTypes{ConstCharPtrTy},
- RetType{CharPtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "strdup", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// char *strndup(const char *s, size_t n);
addToFunctionSummaryMap(
- "strndup", Summary(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy},
- NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(ArgumentCondition(1, WithinRange,
- Range(0, SizeMax))));
+ "strndup",
+ Signature(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
// wchar_t *wcsdup(const wchar_t *s);
- addToFunctionSummaryMap("wcsdup", Summary(ArgTypes{ConstWchar_tPtrTy},
- RetType{Wchar_tPtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "wcsdup", Signature(ArgTypes{ConstWchar_tPtrTy}, RetType{Wchar_tPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// int mkstemp(char *template);
- addToFunctionSummaryMap(
- "mkstemp", Summary(ArgTypes{CharPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap("mkstemp",
+ Signature(ArgTypes{CharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(NotNull(ArgNo(0))));
// char *mkdtemp(char *template);
addToFunctionSummaryMap(
- "mkdtemp", Summary(ArgTypes{CharPtrTy}, RetType{CharPtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ "mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// char *getcwd(char *buf, size_t size);
addToFunctionSummaryMap(
- "getcwd",
- Summary(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}, NoEvalCall)
+ "getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall)
.ArgConstraint(
ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
- if (Mode_tTy) {
- // int mkdir(const char *pathname, mode_t mode);
- addToFunctionSummaryMap("mkdir",
- Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int mkdir(const char *pathname, mode_t mode);
+ addToFunctionSummaryMap(
+ "mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int mkdirat(int dirfd, const char *pathname, mode_t mode);
- addToFunctionSummaryMap(
- "mkdirat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(1))));
- }
+ // int mkdirat(int dirfd, const char *pathname, mode_t mode);
+ addToFunctionSummaryMap(
+ "mkdirat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Dev_tTy = lookupType("dev_t", ACtx);
+ Optional<QualType> Dev_tTy = lookupTy("dev_t");
- if (Mode_tTy && Dev_tTy) {
- // int mknod(const char *pathname, mode_t mode, dev_t dev);
- addToFunctionSummaryMap(
- "mknod", Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy, *Dev_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
-
- // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
- addToFunctionSummaryMap("mknodat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
- *Mode_tTy, *Dev_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(1))));
- }
+ // int mknod(const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap(
+ "mknod",
+ Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- if (Mode_tTy) {
- // int chmod(const char *path, mode_t mode);
- addToFunctionSummaryMap("chmod",
- Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev);
+ addToFunctionSummaryMap(
+ "mknodat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
- // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
- addToFunctionSummaryMap(
- "fchmodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ // int chmod(const char *path, mode_t mode);
+ addToFunctionSummaryMap(
+ "chmod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int fchmod(int fildes, mode_t mode);
- addToFunctionSummaryMap(
- "fchmod",
- Summary(ArgTypes{IntTy, *Mode_tTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- }
+ // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);
+ addToFunctionSummaryMap(
+ "fchmodat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> Uid_tTy = lookupType("uid_t", ACtx);
- Optional<QualType> Gid_tTy = lookupType("gid_t", ACtx);
+ // int fchmod(int fildes, mode_t mode);
+ addToFunctionSummaryMap(
+ "fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- if (Uid_tTy && Gid_tTy) {
- // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
- // int flags);
- addToFunctionSummaryMap(
- "fchownat",
- Summary(ArgTypes{IntTy, ConstCharPtrTy, *Uid_tTy, *Gid_tTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ Optional<QualType> Uid_tTy = lookupTy("uid_t");
+ Optional<QualType> Gid_tTy = lookupTy("gid_t");
- // int chown(const char *path, uid_t owner, gid_t group);
- addToFunctionSummaryMap(
- "chown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group,
+ // int flags);
+ addToFunctionSummaryMap(
+ "fchownat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- // int lchown(const char *path, uid_t owner, gid_t group);
- addToFunctionSummaryMap(
- "lchown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int chown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "chown",
+ Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int fchown(int fildes, uid_t owner, gid_t group);
- addToFunctionSummaryMap(
- "fchown", Summary(ArgTypes{IntTy, *Uid_tTy, *Gid_tTy}, RetType{IntTy},
- NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax))));
- }
+ // int lchown(const char *path, uid_t owner, gid_t group);
+ addToFunctionSummaryMap(
+ "lchown",
+ Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int rmdir(const char *pathname);
+ // int fchown(int fildes, uid_t owner, gid_t group);
addToFunctionSummaryMap(
- "rmdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ "fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // int rmdir(const char *pathname);
+ addToFunctionSummaryMap("rmdir",
+ Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int chdir(const char *path);
- addToFunctionSummaryMap(
- "chdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap("chdir",
+ Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int link(const char *oldpath, const char *newpath);
- addToFunctionSummaryMap("link",
- Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ addToFunctionSummaryMap(
+ "link",
+ Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
// int linkat(int fd1, const char *path1, int fd2, const char *path2,
// int flag);
addToFunctionSummaryMap(
"linkat",
- Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy},
- RetType{IntTy}, NoEvalCall)
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1)))
.ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(3))));
// int unlink(const char *pathname);
- addToFunctionSummaryMap(
- "unlink", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap("unlink",
+ Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
// int unlinkat(int fd, const char *path, int flag);
addToFunctionSummaryMap(
"unlinkat",
- Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy},
- NoEvalCall)
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(NotNull(ArgNo(1))));
- Optional<QualType> StructStatTy = lookupType("stat", ACtx);
- Optional<QualType> StructStatPtrTy, StructStatPtrRestrictTy;
- if (StructStatTy) {
- StructStatPtrTy = ACtx.getPointerType(*StructStatTy);
- StructStatPtrRestrictTy = ACtx.getLangOpts().C99
- ? ACtx.getRestrictType(*StructStatPtrTy)
- : *StructStatPtrTy;
- }
+ Optional<QualType> StructStatTy = lookupTy("stat");
+ Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy);
+ Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy);
- if (StructStatPtrTy)
- // int fstat(int fd, struct stat *statbuf);
- addToFunctionSummaryMap(
- "fstat",
- Summary(ArgTypes{IntTy, *StructStatPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ // int fstat(int fd, struct stat *statbuf);
+ addToFunctionSummaryMap(
+ "fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- if (StructStatPtrRestrictTy) {
- // int stat(const char *restrict path, struct stat *restrict buf);
- addToFunctionSummaryMap(
- "stat",
- Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ // int stat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "stat",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- // int lstat(const char *restrict path, struct stat *restrict buf);
- addToFunctionSummaryMap(
- "lstat",
- Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
-
- // int fstatat(int fd, const char *restrict path,
- // struct stat *restrict buf, int flag);
- addToFunctionSummaryMap(
- "fstatat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
- *StructStatPtrRestrictTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(NotNull(ArgNo(2))));
- }
+ // int lstat(const char *restrict path, struct stat *restrict buf);
+ addToFunctionSummaryMap(
+ "lstat",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- if (DirPtrTy) {
- // DIR *opendir(const char *name);
- addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy},
- RetType{*DirPtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int fstatat(int fd, const char *restrict path,
+ // struct stat *restrict buf, int flag);
+ addToFunctionSummaryMap(
+ "fstatat",
+ Signature(ArgTypes{IntTy, ConstCharPtrRestrictTy,
+ StructStatPtrRestrictTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
- // DIR *fdopendir(int fd);
- addToFunctionSummaryMap(
- "fdopendir", Summary(ArgTypes{IntTy}, RetType{*DirPtrTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax))));
- }
+ // DIR *opendir(const char *name);
+ addToFunctionSummaryMap(
+ "opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // DIR *fdopendir(int fd);
+ addToFunctionSummaryMap("fdopendir",
+ Signature(ArgTypes{IntTy}, RetType{DirPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
// int isatty(int fildes);
addToFunctionSummaryMap(
- "isatty", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ "isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(0, 1))})
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- if (FilePtrTy) {
- // FILE *popen(const char *command, const char *type);
- addToFunctionSummaryMap("popen",
- Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
- RetType{*FilePtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ // FILE *popen(const char *command, const char *type);
+ addToFunctionSummaryMap(
+ "popen",
+ Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- // int pclose(FILE *stream);
- addToFunctionSummaryMap(
- "pclose", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
- }
+ // int pclose(FILE *stream);
+ addToFunctionSummaryMap(
+ "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// int close(int fildes);
- addToFunctionSummaryMap(
- "close", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("close", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(-1, IntMax))));
// long fpathconf(int fildes, int name);
- addToFunctionSummaryMap(
- "fpathconf",
- Summary(ArgTypes{IntTy, IntTy}, RetType{LongTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+ addToFunctionSummaryMap("fpathconf",
+ Signature(ArgTypes{IntTy, IntTy}, RetType{LongTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
// long pathconf(const char *path, int name);
- addToFunctionSummaryMap("pathconf", Summary(ArgTypes{ConstCharPtrTy, IntTy},
- RetType{LongTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- if (FilePtrTy)
- // FILE *fdopen(int fd, const char *mode);
- addToFunctionSummaryMap(
- "fdopen", Summary(ArgTypes{IntTy, ConstCharPtrTy},
- RetType{*FilePtrTy}, NoEvalCall)
- .ArgConstraint(
- ArgumentCondition(0, WithinRange, Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1))));
-
- if (DirPtrTy) {
- // void rewinddir(DIR *dir);
- addToFunctionSummaryMap(
- "rewinddir", Summary(ArgTypes{*DirPtrTy}, RetType{VoidTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // FILE *fdopen(int fd, const char *mode);
+ addToFunctionSummaryMap(
+ "fdopen",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1))));
- // void seekdir(DIR *dirp, long loc);
- addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy},
- RetType{VoidTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
- }
+ // void rewinddir(DIR *dir);
+ addToFunctionSummaryMap(
+ "rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // void seekdir(DIR *dirp, long loc);
+ addToFunctionSummaryMap(
+ "seekdir", Signature(ArgTypes{DirPtrTy, LongTy}, RetType{VoidTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
// int rand_r(unsigned int *seedp);
- addToFunctionSummaryMap("rand_r", Summary(ArgTypes{UnsignedIntPtrTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
-
- // int strcasecmp(const char *s1, const char *s2);
- addToFunctionSummaryMap("strcasecmp",
- Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy},
- RetType{IntTy}, EvalCallAsPure)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ addToFunctionSummaryMap(
+ "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int fileno(FILE *stream);
+ addToFunctionSummaryMap("fileno",
+ Signature(ArgTypes{FilePtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int strncasecmp(const char *s1, const char *s2, size_t n);
+ // int fseeko(FILE *stream, off_t offset, int whence);
addToFunctionSummaryMap(
- "strncasecmp", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, SizeTy},
- RetType{IntTy}, EvalCallAsPure)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(ArgumentCondition(
- 2, WithinRange, Range(0, SizeMax))));
+ "fseeko",
+ Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- if (FilePtrTy && Off_tTy) {
+ // off_t ftello(FILE *stream);
+ addToFunctionSummaryMap(
+ "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
- // int fileno(FILE *stream);
- addToFunctionSummaryMap(
- "fileno", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ // off_t offset);
+ addToFunctionSummaryMap(
+ "mmap",
+ Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy},
+ RetType{VoidPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(-1, IntMax))));
- // int fseeko(FILE *stream, off_t offset, int whence);
- addToFunctionSummaryMap("fseeko",
- Summary(ArgTypes{*FilePtrTy, *Off_tTy, IntTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ Optional<QualType> Off64_tTy = lookupTy("off64_t");
+ // void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
+ // off64_t offset);
+ addToFunctionSummaryMap(
+ "mmap64",
+ Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy},
+ RetType{VoidPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(-1, IntMax))));
- // off_t ftello(FILE *stream);
- addToFunctionSummaryMap(
- "ftello", Summary(ArgTypes{*FilePtrTy}, RetType{*Off_tTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
- }
+ // int pipe(int fildes[2]);
+ addToFunctionSummaryMap("pipe",
+ Signature(ArgTypes{IntPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // off_t lseek(int fildes, off_t offset, int whence);
+ addToFunctionSummaryMap(
+ "lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // ssize_t readlink(const char *restrict path, char *restrict buf,
+ // size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlink",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2)))
+ .ArgConstraint(
+ ArgumentCondition(2, WithinRange, Range(0, SizeMax))));
+
+ // ssize_t readlinkat(int fd, const char *restrict path,
+ // char *restrict buf, size_t bufsize);
+ addToFunctionSummaryMap(
+ "readlinkat",
+ Signature(
+ ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(3)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
+ /*BufSize=*/ArgNo(3)))
+ .ArgConstraint(
+ ArgumentCondition(3, WithinRange, Range(0, SizeMax))));
+
+ // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char
+ // *newpath);
+ addToFunctionSummaryMap(
+ "renameat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // char *realpath(const char *restrict file_name,
+ // char *restrict resolved_name);
+ addToFunctionSummaryMap(
+ "realpath",
+ Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
+ RetType{CharPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy));
+
+ // int execv(const char *path, char *const argv[]);
+ addToFunctionSummaryMap(
+ "execv",
+ Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(-1))})
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int execvp(const char *file, char *const argv[]);
+ addToFunctionSummaryMap(
+ "execvp",
+ Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, SingleValue(-1))})
+ .ArgConstraint(NotNull(ArgNo(0))));
- if (Off_tTy) {
- Optional<RangeInt> Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
+ // int getopt(int argc, char * const argv[], const char *optstring);
+ addToFunctionSummaryMap(
+ "getopt",
+ Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2))));
- // void *mmap(void *addr, size_t length, int prot, int flags, int fd,
- // off_t offset);
+ Optional<QualType> StructSockaddrTy = lookupTy("sockaddr");
+ Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy);
+ Optional<QualType> ConstStructSockaddrPtrTy =
+ getPointerTy(getConstTy(StructSockaddrTy));
+ Optional<QualType> StructSockaddrPtrRestrictTy =
+ getRestrictTy(StructSockaddrPtrTy);
+ Optional<QualType> ConstStructSockaddrPtrRestrictTy =
+ getRestrictTy(ConstStructSockaddrPtrTy);
+ Optional<QualType> Socklen_tTy = lookupTy("socklen_t");
+ Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy);
+ Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy);
+ Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy);
+
+ // In 'socket.h' of some libc implementations with C99, sockaddr parameter
+ // is a transparent union of the underlying sockaddr_ family of pointers
+ // instead of being a pointer to struct sockaddr. In these cases, the
+ // standardized signature will not match, thus we try to match with another
+ // signature that has the joker Irrelevant type. We also remove those
+ // constraints which require pointer types for the sockaddr param.
+ auto Accept =
+ Summary(NoEvalCall)
+ .Case(ReturnsFileDescriptor)
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)));
+ if (!addToFunctionSummaryMap(
+ "accept",
+ // int accept(int socket, struct sockaddr *restrict address,
+ // socklen_t *restrict address_len);
+ Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy,
+ Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Accept))
+ addToFunctionSummaryMap(
+ "accept",
+ Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Accept);
+
+ // int bind(int socket, const struct sockaddr *address, socklen_t
+ // address_len);
+ if (!addToFunctionSummaryMap(
+ "bind",
+ Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2)))
+ .ArgConstraint(
+ ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax)))))
+ // Do not add constraints on sockaddr.
addToFunctionSummaryMap(
- "mmap",
- Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off_tTy},
- RetType{VoidPtrTy}, NoEvalCall)
+ "bind",
+ Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(
- ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
.ArgConstraint(
- ArgumentCondition(4, WithinRange, Range(0, *Off_tMax))));
- }
+ ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax))));
+
+ // int getpeername(int socket, struct sockaddr *restrict address,
+ // socklen_t *restrict address_len);
+ if (!addToFunctionSummaryMap(
+ "getpeername",
+ Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy,
+ Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2)))))
+ addToFunctionSummaryMap(
+ "getpeername",
+ Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- Optional<QualType> Off64_tTy = lookupType("off64_t", ACtx);
- Optional<RangeInt> Off64_tMax;
- if (Off64_tTy) {
- Off64_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue();
- // void *mmap64(void *addr, size_t length, int prot, int flags, int fd,
- // off64_t offset);
+ // int getsockname(int socket, struct sockaddr *restrict address,
+ // socklen_t *restrict address_len);
+ if (!addToFunctionSummaryMap(
+ "getsockname",
+ Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy,
+ Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(NotNull(ArgNo(2)))))
addToFunctionSummaryMap(
- "mmap64",
- Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off64_tTy},
- RetType{VoidPtrTy}, NoEvalCall)
+ "getsockname",
+ Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(
- ArgumentCondition(1, WithinRange, Range(1, SizeMax)))
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // int connect(int socket, const struct sockaddr *address, socklen_t
+ // address_len);
+ if (!addToFunctionSummaryMap(
+ "connect",
+ Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(NotNull(ArgNo(1)))))
+ addToFunctionSummaryMap(
+ "connect",
+ Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
.ArgConstraint(
- ArgumentCondition(4, WithinRange, Range(0, *Off64_tMax))));
- }
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- // int pipe(int fildes[2]);
+ auto Recvfrom =
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2)));
+ if (!addToFunctionSummaryMap(
+ "recvfrom",
+ // ssize_t recvfrom(int socket, void *restrict buffer,
+ // size_t length,
+ // int flags, struct sockaddr *restrict address,
+ // socklen_t *restrict address_len);
+ Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy,
+ StructSockaddrPtrRestrictTy,
+ Socklen_tPtrRestrictTy},
+ RetType{Ssize_tTy}),
+ Recvfrom))
+ addToFunctionSummaryMap(
+ "recvfrom",
+ Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy,
+ Irrelevant, Socklen_tPtrRestrictTy},
+ RetType{Ssize_tTy}),
+ Recvfrom);
+
+ auto Sendto =
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2)));
+ if (!addToFunctionSummaryMap(
+ "sendto",
+ // ssize_t sendto(int socket, const void *message, size_t length,
+ // int flags, const struct sockaddr *dest_addr,
+ // socklen_t dest_len);
+ Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy,
+ ConstStructSockaddrPtrTy, Socklen_tTy},
+ RetType{Ssize_tTy}),
+ Sendto))
+ addToFunctionSummaryMap(
+ "sendto",
+ Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant,
+ Socklen_tTy},
+ RetType{Ssize_tTy}),
+ Sendto);
+
+ // int listen(int sockfd, int backlog);
+ addToFunctionSummaryMap("listen",
+ Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(ArgumentCondition(
+ 0, WithinRange, Range(0, IntMax))));
+
+ // ssize_t recv(int sockfd, void *buf, size_t len, int flags);
addToFunctionSummaryMap(
- "pipe", Summary(ArgTypes{IntPtrTy}, RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ "recv",
+ Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2))));
- if (Off_tTy)
- // off_t lseek(int fildes, off_t offset, int whence);
- addToFunctionSummaryMap(
- "lseek", Summary(ArgTypes{IntTy, *Off_tTy, IntTy}, RetType{*Off_tTy},
- NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax))));
+ Optional<QualType> StructMsghdrTy = lookupTy("msghdr");
+ Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy);
+ Optional<QualType> ConstStructMsghdrPtrTy =
+ getPointerTy(getConstTy(StructMsghdrTy));
- Optional<QualType> Ssize_tTy = lookupType("ssize_t", ACtx);
+ // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
+ addToFunctionSummaryMap(
+ "recvmsg",
+ Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- if (Ssize_tTy) {
- // ssize_t readlink(const char *restrict path, char *restrict buf,
- // size_t bufsize);
- addToFunctionSummaryMap(
- "readlink",
- Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy},
- RetType{*Ssize_tTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
- /*BufSize=*/ArgNo(2)))
- .ArgConstraint(
- ArgumentCondition(2, WithinRange, Range(0, SizeMax))));
+ // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
+ addToFunctionSummaryMap(
+ "sendmsg",
+ Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
- // ssize_t readlinkat(int fd, const char *restrict path,
- // char *restrict buf, size_t bufsize);
- addToFunctionSummaryMap(
- "readlinkat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy,
- CharPtrRestrictTy, SizeTy},
- RetType{*Ssize_tTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange,
- Range(0, IntMax)))
- .ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(NotNull(ArgNo(2)))
- .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2),
- /*BufSize=*/ArgNo(3)))
- .ArgConstraint(ArgumentCondition(
- 3, WithinRange, Range(0, SizeMax))));
- }
+ // int setsockopt(int socket, int level, int option_name,
+ // const void *option_value, socklen_t option_len);
+ addToFunctionSummaryMap(
+ "setsockopt",
+ Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(3)))
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4)))
+ .ArgConstraint(
+ ArgumentCondition(4, WithinRange, Range(0, Socklen_tMax))));
- // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char
- // *newpath);
- addToFunctionSummaryMap("renameat", Summary(ArgTypes{IntTy, ConstCharPtrTy,
- IntTy, ConstCharPtrTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(1)))
- .ArgConstraint(NotNull(ArgNo(3))));
+ // int getsockopt(int socket, int level, int option_name,
+ // void *restrict option_value,
+ // socklen_t *restrict option_len);
+ addToFunctionSummaryMap(
+ "getsockopt",
+ Signature(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy,
+ Socklen_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(3)))
+ .ArgConstraint(NotNull(ArgNo(4))));
+
+ // ssize_t send(int sockfd, const void *buf, size_t len, int flags);
+ addToFunctionSummaryMap(
+ "send",
+ Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy},
+ RetType{Ssize_tTy}),
+ Summary(NoEvalCall)
+ .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)),
+ ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))})
+ .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*BufSize=*/ArgNo(2))));
- // char *realpath(const char *restrict file_name,
- // char *restrict resolved_name);
+ // int socketpair(int domain, int type, int protocol, int sv[2]);
+ addToFunctionSummaryMap(
+ "socketpair",
+ Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(3))));
+
+ // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,
+ // char *restrict node, socklen_t nodelen,
+ // char *restrict service,
+ // socklen_t servicelen, int flags);
+ //
+ // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr
+ // parameter is never handled as a transparent union in netdb.h
addToFunctionSummaryMap(
- "realpath", Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy},
- RetType{CharPtrTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ "getnameinfo",
+ Signature(ArgTypes{ConstStructSockaddrPtrRestrictTy, Socklen_tTy,
+ CharPtrRestrictTy, Socklen_tTy, CharPtrRestrictTy,
+ Socklen_tTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, Socklen_tMax)))
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3)))
+ .ArgConstraint(
+ ArgumentCondition(3, WithinRange, Range(0, Socklen_tMax)))
+ .ArgConstraint(
+ BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5)))
+ .ArgConstraint(
+ ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax))));
- QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst());
+ Optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
+ Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
- // int execv(const char *path, char *const argv[]);
- addToFunctionSummaryMap("execv",
- Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ // int utime(const char *filename, struct utimbuf *buf);
+ addToFunctionSummaryMap(
+ "utime",
+ Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
- // int execvp(const char *file, char *const argv[]);
- addToFunctionSummaryMap("execvp",
- Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(NotNull(ArgNo(0))));
+ Optional<QualType> StructTimespecTy = lookupTy("timespec");
+ Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy);
+ Optional<QualType> ConstStructTimespecPtrTy =
+ getPointerTy(getConstTy(StructTimespecTy));
- // int getopt(int argc, char * const argv[], const char *optstring);
+ // int futimens(int fd, const struct timespec times[2]);
addToFunctionSummaryMap(
- "getopt",
- Summary(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy},
- RetType{IntTy}, NoEvalCall)
- .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax)))
+ "futimens",
+ Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(
+ ArgumentCondition(0, WithinRange, Range(0, IntMax))));
+
+ // int utimensat(int dirfd, const char *pathname,
+ // const struct timespec times[2], int flags);
+ addToFunctionSummaryMap("utimensat",
+ Signature(ArgTypes{IntTy, ConstCharPtrTy,
+ ConstStructTimespecPtrTy, IntTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructTimevalTy = lookupTy("timeval");
+ Optional<QualType> ConstStructTimevalPtrTy =
+ getPointerTy(getConstTy(StructTimevalTy));
+
+ // int utimes(const char *filename, const struct timeval times[2]);
+ addToFunctionSummaryMap(
+ "utimes",
+ Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+ addToFunctionSummaryMap(
+ "nanosleep",
+ Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ Optional<QualType> Time_tTy = lookupTy("time_t");
+ Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy));
+ Optional<QualType> ConstTime_tPtrRestrictTy =
+ getRestrictTy(ConstTime_tPtrTy);
+
+ Optional<QualType> StructTmTy = lookupTy("tm");
+ Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
+ Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy);
+ Optional<QualType> ConstStructTmPtrTy =
+ getPointerTy(getConstTy(StructTmTy));
+ Optional<QualType> ConstStructTmPtrRestrictTy =
+ getRestrictTy(ConstStructTmPtrTy);
+
+ // struct tm * localtime(const time_t *tp);
+ addToFunctionSummaryMap(
+ "localtime",
+ Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // struct tm *localtime_r(const time_t *restrict timer,
+ // struct tm *restrict result);
+ addToFunctionSummaryMap(
+ "localtime_r",
+ Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
+ RetType{StructTmPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // char *asctime_r(const struct tm *restrict tm, char *restrict buf);
+ addToFunctionSummaryMap(
+ "asctime_r",
+ Signature(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy},
+ RetType{CharPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*MinBufSize=*/BVF.getValue(26, IntTy))));
+
+ // char *ctime_r(const time_t *timep, char *buf);
+ addToFunctionSummaryMap(
+ "ctime_r",
+ Signature(ArgTypes{ConstTime_tPtrTy, CharPtrTy}, RetType{CharPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(
+ /*Buffer=*/ArgNo(1),
+ /*MinBufSize=*/BVF.getValue(26, IntTy))));
+
+ // struct tm *gmtime_r(const time_t *restrict timer,
+ // struct tm *restrict result);
+ addToFunctionSummaryMap(
+ "gmtime_r",
+ Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
+ RetType{StructTmPtrTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // struct tm * gmtime(const time_t *tp);
+ addToFunctionSummaryMap(
+ "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ Optional<QualType> Clockid_tTy = lookupTy("clockid_t");
+
+ // int clock_gettime(clockid_t clock_id, struct timespec *tp);
+ addToFunctionSummaryMap(
+ "clock_gettime",
+ Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructItimervalTy = lookupTy("itimerval");
+ Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy);
+
+ // int getitimer(int which, struct itimerval *curr_value);
+ addToFunctionSummaryMap(
+ "getitimer",
+ Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .Case(ReturnsZeroOrMinusOne)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t");
+ Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy);
+ Optional<QualType> Pthread_tTy = lookupTy("pthread_t");
+ Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy);
+ Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy);
+ Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t");
+ Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy);
+ Optional<QualType> Pthread_mutex_tPtrRestrictTy =
+ getRestrictTy(Pthread_mutex_tPtrTy);
+ Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t");
+ Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy);
+ Optional<QualType> ConstPthread_attr_tPtrTy =
+ getPointerTy(getConstTy(Pthread_attr_tTy));
+ Optional<QualType> ConstPthread_attr_tPtrRestrictTy =
+ getRestrictTy(ConstPthread_attr_tPtrTy);
+ Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t");
+ Optional<QualType> ConstPthread_mutexattr_tPtrTy =
+ getPointerTy(getConstTy(Pthread_mutexattr_tTy));
+ Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy =
+ getRestrictTy(ConstPthread_mutexattr_tPtrTy);
+
+ QualType PthreadStartRoutineTy = getPointerTy(
+ ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy,
+ FunctionProtoType::ExtProtoInfo()));
+
+ // int pthread_cond_signal(pthread_cond_t *cond);
+ // int pthread_cond_broadcast(pthread_cond_t *cond);
+ addToFunctionSummaryMap(
+ {"pthread_cond_signal", "pthread_cond_broadcast"},
+ Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int pthread_create(pthread_t *restrict thread,
+ // const pthread_attr_t *restrict attr,
+ // void *(*start_routine)(void*), void *restrict arg);
+ addToFunctionSummaryMap(
+ "pthread_create",
+ Signature(ArgTypes{Pthread_tPtrRestrictTy,
+ ConstPthread_attr_tPtrRestrictTy,
+ PthreadStartRoutineTy, VoidPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(2))));
+
+ // int pthread_attr_destroy(pthread_attr_t *attr);
+ // int pthread_attr_init(pthread_attr_t *attr);
+ addToFunctionSummaryMap(
+ {"pthread_attr_destroy", "pthread_attr_init"},
+ Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr,
+ // size_t *restrict stacksize);
+ // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,
+ // size_t *restrict guardsize);
+ addToFunctionSummaryMap(
+ {"pthread_attr_getstacksize", "pthread_attr_getguardsize"},
+ Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
+ // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
+ addToFunctionSummaryMap(
+ {"pthread_attr_setstacksize", "pthread_attr_setguardsize"},
+ Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}),
+ Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(
+ ArgumentCondition(1, WithinRange, Range(0, SizeMax))));
+
+ // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const
+ // pthread_mutexattr_t *restrict attr);
+ addToFunctionSummaryMap(
+ "pthread_mutex_init",
+ Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy,
+ ConstPthread_mutexattr_tPtrRestrictTy},
+ RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
+
+ // int pthread_mutex_destroy(pthread_mutex_t *mutex);
+ // int pthread_mutex_lock(pthread_mutex_t *mutex);
+ // int pthread_mutex_trylock(pthread_mutex_t *mutex);
+ // int pthread_mutex_unlock(pthread_mutex_t *mutex);
+ addToFunctionSummaryMap(
+ {"pthread_mutex_destroy", "pthread_mutex_lock", "pthread_mutex_trylock",
+ "pthread_mutex_unlock"},
+ Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}),
+ Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));
}
// Functions for testing.
if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) {
addToFunctionSummaryMap(
"__two_constrained_args",
- Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, EvalCallAsPure)
+ Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))
.ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1))));
addToFunctionSummaryMap(
- "__arg_constrained_twice",
- Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure)
+ "__arg_constrained_twice", Signature(ArgTypes{IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))
.ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2))));
addToFunctionSummaryMap(
"__defaultparam",
- Summary(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}, EvalCallAsPure)
- .ArgConstraint(NotNull(ArgNo(0))));
- addToFunctionSummaryMap("__variadic",
- Summary(ArgTypes{VoidPtrTy, ConstCharPtrTy},
- RetType{IntTy}, EvalCallAsPure)
- .ArgConstraint(NotNull(ArgNo(0)))
- .ArgConstraint(NotNull(ArgNo(1))));
+ Signature(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0))));
+ addToFunctionSummaryMap(
+ "__variadic",
+ Signature(ArgTypes{VoidPtrTy, ConstCharPtrTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
addToFunctionSummaryMap(
"__buf_size_arg_constraint",
- Summary(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy},
- EvalCallAsPure)
+ Signature(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.ArgConstraint(
BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))));
addToFunctionSummaryMap(
"__buf_size_arg_constraint_mul",
- Summary(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy},
- EvalCallAsPure)
+ Signature(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
/*BufSizeMultiplier=*/ArgNo(2))));
+ addToFunctionSummaryMap(
+ "__buf_size_arg_constraint_concrete",
+ Signature(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}),
+ Summary(EvalCallAsPure)
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0),
+ /*BufSize=*/BVF.getValue(10, IntTy))));
+ addToFunctionSummaryMap(
+ {"__test_restrict_param_0", "__test_restrict_param_1",
+ "__test_restrict_param_2"},
+ Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}),
+ Summary(EvalCallAsPure));
}
}
@@ -1749,7 +2496,8 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
}
-bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) {
+bool ento::shouldRegisterStdCLibraryFunctionsChecker(
+ const CheckerManager &mgr) {
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index f6abbe4f8f03..6b176b3c4e2b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -204,7 +204,8 @@ class StreamChecker : public Checker<check::PreCall, eval::Call,
BugType BT_IllegalWhence{this, "Illegal whence argument",
"Stream handling error"};
BugType BT_StreamEof{this, "Stream already in EOF", "Stream handling error"};
- BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error"};
+ BugType BT_ResourceLeak{this, "Resource leak", "Stream handling error",
+ /*SuppressOnSink =*/true};
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -337,6 +338,12 @@ private:
/// to ensure uniform handling.
void reportFEofWarning(CheckerContext &C, ProgramStateRef State) const;
+ /// Emit resource leak warnings for the given symbols.
+ /// Createn a non-fatal error node for these, and returns it (if any warnings
+ /// were generated). Return value is non-null.
+ ExplodedNode *reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
+ CheckerContext &C, ExplodedNode *Pred) const;
+
/// Find the description data of the function called by a call event.
/// Returns nullptr if no function is recognized.
const FnDescription *lookupFn(const CallEvent &Call) const {
@@ -956,28 +963,14 @@ void StreamChecker::reportFEofWarning(CheckerContext &C,
C.addTransition(State);
}
-void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
-
- // TODO: Clean up the state.
- const StreamMapTy &Map = State->get<StreamMap>();
- for (const auto &I : Map) {
- SymbolRef Sym = I.first;
- const StreamState &SS = I.second;
- if (!SymReaper.isDead(Sym) || !SS.isOpened())
- continue;
-
- ExplodedNode *N = C.generateErrorNode();
- if (!N)
- continue;
-
- // Do not warn for non-closed stream at program exit.
- ExplodedNode *Pred = C.getPredecessor();
- if (Pred && Pred->getCFGBlock() &&
- Pred->getCFGBlock()->hasNoReturnElement())
- continue;
+ExplodedNode *
+StreamChecker::reportLeaks(const SmallVector<SymbolRef, 2> &LeakedSyms,
+ CheckerContext &C, ExplodedNode *Pred) const {
+ ExplodedNode *Err = C.generateNonFatalErrorNode(C.getState(), Pred);
+ if (!Err)
+ return Pred;
+ for (SymbolRef LeakSym : LeakedSyms) {
// Resource leaks can result in multiple warning that describe the same kind
// of programming error:
// void f() {
@@ -989,8 +982,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// from a different kinds of errors), the reduction in redundant reports
// makes this a worthwhile heuristic.
// FIXME: Add a checker option to turn this uniqueing feature off.
-
- const ExplodedNode *StreamOpenNode = getAcquisitionSite(N, Sym, C);
+ const ExplodedNode *StreamOpenNode = getAcquisitionSite(Err, LeakSym, C);
assert(StreamOpenNode && "Could not find place of stream opening.");
PathDiagnosticLocation LocUsedForUniqueing =
PathDiagnosticLocation::createBegin(
@@ -1000,12 +992,38 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
std::unique_ptr<PathSensitiveBugReport> R =
std::make_unique<PathSensitiveBugReport>(
BT_ResourceLeak,
- "Opened stream never closed. Potential resource leak.", N,
+ "Opened stream never closed. Potential resource leak.", Err,
LocUsedForUniqueing,
StreamOpenNode->getLocationContext()->getDecl());
- R->markInteresting(Sym);
+ R->markInteresting(LeakSym);
C.emitReport(std::move(R));
}
+
+ return Err;
+}
+
+void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ llvm::SmallVector<SymbolRef, 2> LeakedSyms;
+
+ const StreamMapTy &Map = State->get<StreamMap>();
+ for (const auto &I : Map) {
+ SymbolRef Sym = I.first;
+ const StreamState &SS = I.second;
+ if (!SymReaper.isDead(Sym))
+ continue;
+ if (SS.isOpened())
+ LeakedSyms.push_back(Sym);
+ State = State->remove<StreamMap>(Sym);
+ }
+
+ ExplodedNode *N = C.getPredecessor();
+ if (!LeakedSyms.empty())
+ N = reportLeaks(LeakedSyms, C, N);
+
+ C.addTransition(State, N);
}
ProgramStateRef StreamChecker::checkPointerEscape(
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
index 5b46ffb656cf..71b2ab834a07 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -148,7 +148,7 @@ bool taint::isTainted(ProgramStateRef State, const Stmt *S,
}
bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) {
- if (const SymExpr *Sym = V.getAsSymExpr())
+ if (SymbolRef Sym = V.getAsSymbol())
return isTainted(State, Sym, Kind);
if (const MemRegion *Reg = V.getAsRegion())
return isTainted(State, Reg, Kind);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index 3e0caaf79ca0..ebe5ad53cc30 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/StmtObjC.h"
+#include "clang/AST/Type.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -54,10 +56,13 @@ public:
void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
};
-}
+} // namespace
void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
CheckerContext &Ctx) const {
+ // ObjCForCollection is a loop, but has no actual condition.
+ if (isa<ObjCForCollectionStmt>(Condition))
+ return;
SVal X = Ctx.getSVal(Condition);
if (X.isUndef()) {
// Generate a sink node, which implicitly marks both outgoing branches as
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index f49ee5fa5ad3..1c589e3468c2 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -125,8 +125,8 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call,
OS << "Call to ";
if (IsPure)
OS << "pure ";
- OS << "virtual method '" << MD->getParent()->getNameAsString()
- << "::" << MD->getNameAsString() << "' during ";
+ OS << "virtual method '" << MD->getParent()->getDeclName()
+ << "::" << MD->getDeclName() << "' during ";
if (*ObState == ObjectState::CtorCalled)
OS << "construction ";
else
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 34c072ac2241..9c7a59971763 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -34,7 +34,9 @@ tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
}
if (auto *call = dyn_cast<CallExpr>(E)) {
if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
- if (isGetterOfRefCounted(memberCall->getMethodDecl())) {
+ Optional<bool> IsGetterOfRefCt =
+ isGetterOfRefCounted(memberCall->getMethodDecl());
+ if (IsGetterOfRefCt && *IsGetterOfRefCt) {
E = memberCall->getImplicitObjectArgument();
if (StopAtFirstRefCountedObj) {
return {E, true};
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
index 3956db933b35..97f75135bf92 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -76,8 +76,11 @@ public:
if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
// If we don't see the definition we just don't know.
- if (MemberCXXRD->hasDefinition() && isRefCountable(MemberCXXRD))
- reportBug(Member, MemberType, MemberCXXRD, RD);
+ if (MemberCXXRD->hasDefinition()) {
+ llvm::Optional<bool> isRCAble = isRefCountable(MemberCXXRD);
+ if (isRCAble && *isRCAble)
+ reportBug(Member, MemberType, MemberCXXRD, RD);
+ }
}
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 168cfd511170..a198943c9433 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -12,6 +12,7 @@
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
+#include "llvm/ADT/Optional.h"
using llvm::Optional;
using namespace clang;
@@ -20,6 +21,7 @@ namespace {
bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
assert(R);
+ assert(R->hasDefinition());
bool hasRef = false;
bool hasDeref = false;
@@ -43,25 +45,29 @@ bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
namespace clang {
-const CXXRecordDecl *isRefCountable(const CXXBaseSpecifier *Base) {
+llvm::Optional<const clang::CXXRecordDecl *>
+isRefCountable(const CXXBaseSpecifier *Base) {
assert(Base);
const Type *T = Base->getType().getTypePtrOrNull();
if (!T)
- return nullptr;
+ return llvm::None;
const CXXRecordDecl *R = T->getAsCXXRecordDecl();
if (!R)
- return nullptr;
+ return llvm::None;
+ if (!R->hasDefinition())
+ return llvm::None;
return hasPublicRefAndDeref(R) ? R : nullptr;
}
-bool isRefCountable(const CXXRecordDecl *R) {
+llvm::Optional<bool> isRefCountable(const CXXRecordDecl *R) {
assert(R);
R = R->getDefinition();
- assert(R);
+ if (!R)
+ return llvm::None;
if (hasPublicRefAndDeref(R))
return true;
@@ -69,13 +75,24 @@ bool isRefCountable(const CXXRecordDecl *R) {
CXXBasePaths Paths;
Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
- const auto isRefCountableBase = [](const CXXBaseSpecifier *Base,
- CXXBasePath &) {
- return clang::isRefCountable(Base);
- };
+ bool AnyInconclusiveBase = false;
+ const auto isRefCountableBase =
+ [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+ Optional<const clang::CXXRecordDecl *> IsRefCountable =
+ clang::isRefCountable(Base);
+ if (!IsRefCountable) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*IsRefCountable) != nullptr;
+ };
+
+ bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+ /*LookupInDependent =*/true);
+ if (AnyInconclusiveBase)
+ return llvm::None;
- return R->lookupInBases(isRefCountableBase, Paths,
- /*LookupInDependent =*/true);
+ return BasesResult;
}
bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
@@ -95,12 +112,19 @@ bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
|| FunctionName == "Identifier";
}
-bool isUncounted(const CXXRecordDecl *Class) {
+llvm::Optional<bool> isUncounted(const CXXRecordDecl *Class) {
// Keep isRefCounted first as it's cheaper.
- return !isRefCounted(Class) && isRefCountable(Class);
+ if (isRefCounted(Class))
+ return false;
+
+ llvm::Optional<bool> IsRefCountable = isRefCountable(Class);
+ if (!IsRefCountable)
+ return llvm::None;
+
+ return (*IsRefCountable);
}
-bool isUncountedPtr(const Type *T) {
+llvm::Optional<bool> isUncountedPtr(const Type *T) {
assert(T);
if (T->isPointerType() || T->isReferenceType()) {
@@ -111,7 +135,7 @@ bool isUncountedPtr(const Type *T) {
return false;
}
-bool isGetterOfRefCounted(const CXXMethodDecl *M) {
+Optional<bool> isGetterOfRefCounted(const CXXMethodDecl *M) {
assert(M);
if (isa<CXXMethodDecl>(M)) {
@@ -133,9 +157,7 @@ bool isGetterOfRefCounted(const CXXMethodDecl *M) {
if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
if (auto *targetConversionType =
maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
- if (isUncountedPtr(targetConversionType)) {
- return true;
- }
+ return isUncountedPtr(targetConversionType);
}
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 83d9c0bcc13b..730a59977175 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -9,6 +9,8 @@
#ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+#include "llvm/ADT/APInt.h"
+
namespace clang {
class CXXBaseSpecifier;
class CXXMethodDecl;
@@ -25,30 +27,31 @@ class Type;
// Ref<T>.
/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
-/// not.
-const clang::CXXRecordDecl *isRefCountable(const clang::CXXBaseSpecifier *Base);
+/// not, None if inconclusive.
+llvm::Optional<const clang::CXXRecordDecl *>
+isRefCountable(const clang::CXXBaseSpecifier *Base);
-/// \returns true if \p Class is ref-countable, false if not.
-/// Asserts that \p Class IS a definition.
-bool isRefCountable(const clang::CXXRecordDecl *Class);
+/// \returns true if \p Class is ref-countable, false if not, None if
+/// inconclusive.
+llvm::Optional<bool> isRefCountable(const clang::CXXRecordDecl *Class);
/// \returns true if \p Class is ref-counted, false if not.
bool isRefCounted(const clang::CXXRecordDecl *Class);
/// \returns true if \p Class is ref-countable AND not ref-counted, false if
-/// not. Asserts that \p Class IS a definition.
-bool isUncounted(const clang::CXXRecordDecl *Class);
+/// not, None if inconclusive.
+llvm::Optional<bool> isUncounted(const clang::CXXRecordDecl *Class);
/// \returns true if \p T is either a raw pointer or reference to an uncounted
-/// class, false if not.
-bool isUncountedPtr(const clang::Type *T);
+/// class, false if not, None if inconclusive.
+llvm::Optional<bool> isUncountedPtr(const clang::Type *T);
/// \returns true if \p F creates ref-countable object from uncounted parameter,
/// false if not.
bool isCtorOfRefCounted(const clang::FunctionDecl *F);
/// \returns true if \p M is getter of a ref-counted class, false if not.
-bool isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
+llvm::Optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
/// \returns true if \p F is a conversion between ref-countable or ref-counted
/// pointer types.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 81ce284c2dc7..fa9ece217cc0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -76,19 +76,15 @@ public:
(AccSpec == AS_none && RD->isClass()))
return false;
- llvm::Optional<const clang::CXXRecordDecl *> MaybeRefCntblBaseRD =
+ llvm::Optional<const CXXRecordDecl *> RefCntblBaseRD =
isRefCountable(Base);
- if (!MaybeRefCntblBaseRD.hasValue())
+ if (!RefCntblBaseRD || !(*RefCntblBaseRD))
return false;
- const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue();
- if (!RefCntblBaseRD)
- return false;
-
- const auto *Dtor = RefCntblBaseRD->getDestructor();
+ const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
if (!Dtor || !Dtor->isVirtual()) {
ProblematicBaseSpecifier = Base;
- ProblematicBaseClass = RefCntblBaseRD;
+ ProblematicBaseClass = *RefCntblBaseRD;
return true;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index 940a1f349831..d70bd9489d2c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -86,7 +86,8 @@ public:
continue; // FIXME? Should we bail?
// FIXME: more complex types (arrays, references to raw pointers, etc)
- if (!isUncountedPtr(ArgType))
+ Optional<bool> IsUncounted = isUncountedPtr(ArgType);
+ if (!IsUncounted || !(*IsUncounted))
continue;
const auto *Arg = CE->getArg(ArgIdx);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
new file mode 100644
index 000000000000..deebbd603b2c
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -0,0 +1,107 @@
+//=======- UncountedLambdaCapturesChecker.cpp --------------------*- C++ -*-==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UncountedLambdaCapturesChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug{this, "Lambda capture of uncounted variable",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedLambdaCapturesChecker *Checker;
+ explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitLambdaExpr(LambdaExpr *L) {
+ Checker->visitLambdaExpr(L);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitLambdaExpr(LambdaExpr *L) const {
+ for (const LambdaCapture &C : L->captures()) {
+ if (C.capturesVariable()) {
+ VarDecl *CapturedVar = C.getCapturedVar();
+ if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
+ Optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ reportBug(C, CapturedVar, CapturedVarType);
+ }
+ }
+ }
+ }
+ }
+
+ void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
+ const Type *T) const {
+ assert(CapturedVar);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ if (Capture.isExplicit()) {
+ Os << "Captured ";
+ } else {
+ Os << "Implicitly captured ";
+ }
+ if (T->isPointerType()) {
+ Os << "raw-pointer ";
+ } else {
+ assert(T->isReferenceType());
+ Os << "reference ";
+ }
+
+ printQuotedQualifiedName(Os, Capture.getCapturedVar());
+ Os << " to uncounted type is unsafe.";
+
+ PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedLambdaCapturesChecker>();
+}
+
+bool ento::shouldRegisterUncountedLambdaCapturesChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
new file mode 100644
index 000000000000..7e86f28cb70f
--- /dev/null
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
@@ -0,0 +1,251 @@
+//=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// for ( int a = ...) ... true
+// for ( int a : ...) ... true
+// if ( int* a = ) ... true
+// anything else ... false
+bool isDeclaredInForOrIf(const VarDecl *Var) {
+ assert(Var);
+ auto &ASTCtx = Var->getASTContext();
+ auto parent = ASTCtx.getParents(*Var);
+
+ if (parent.size() == 1) {
+ if (auto *DS = parent.begin()->get<DeclStmt>()) {
+ DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
+ if (grandParent.size() == 1) {
+ return grandParent.begin()->get<ForStmt>() ||
+ grandParent.begin()->get<IfStmt>() ||
+ grandParent.begin()->get<CXXForRangeStmt>();
+ }
+ }
+ }
+ return false;
+}
+
+// FIXME: should be defined by anotations in the future
+bool isRefcountedStringsHack(const VarDecl *V) {
+ assert(V);
+ auto safeClass = [](const std::string &className) {
+ return className == "String" || className == "AtomString" ||
+ className == "UniquedString" || className == "Identifier";
+ };
+ QualType QT = V->getType();
+ auto *T = QT.getTypePtr();
+ if (auto *CXXRD = T->getAsCXXRecordDecl()) {
+ if (safeClass(safeGetName(CXXRD)))
+ return true;
+ }
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
+ if (safeClass(safeGetName(CXXRD)))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
+ const VarDecl *MaybeGuardian) {
+ assert(Guarded);
+ assert(MaybeGuardian);
+
+ if (!MaybeGuardian->isLocalVarDecl())
+ return false;
+
+ const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
+
+ ASTContext &ctx = MaybeGuardian->getASTContext();
+
+ for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
+ !guardianAncestors.empty();
+ guardianAncestors = ctx.getParents(
+ *guardianAncestors
+ .begin()) // FIXME - should we handle all of the parents?
+ ) {
+ for (auto &guardianAncestor : guardianAncestors) {
+ if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
+ guardiansClosestCompStmtAncestor = CStmtParentAncestor;
+ break;
+ }
+ }
+ if (guardiansClosestCompStmtAncestor)
+ break;
+ }
+
+ if (!guardiansClosestCompStmtAncestor)
+ return false;
+
+ // We need to skip the first CompoundStmt to avoid situation when guardian is
+ // defined in the same scope as guarded variable.
+ bool HaveSkippedFirstCompoundStmt = false;
+ for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
+ !guardedVarAncestors.empty();
+ guardedVarAncestors = ctx.getParents(
+ *guardedVarAncestors
+ .begin()) // FIXME - should we handle all of the parents?
+ ) {
+ for (auto &guardedVarAncestor : guardedVarAncestors) {
+ if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
+ if (!HaveSkippedFirstCompoundStmt) {
+ HaveSkippedFirstCompoundStmt = true;
+ continue;
+ }
+ if (CStmtAncestor == guardiansClosestCompStmtAncestor)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+class UncountedLocalVarsChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+ BugType Bug{this,
+ "Uncounted raw pointer or reference not provably backed by "
+ "ref-counted variable",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedLocalVarsChecker *Checker;
+ explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitVarDecl(VarDecl *V) {
+ Checker->visitVarDecl(V);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitVarDecl(const VarDecl *V) const {
+ if (shouldSkipVarDecl(V))
+ return;
+
+ const auto *ArgType = V->getType().getTypePtr();
+ if (!ArgType)
+ return;
+
+ Optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ const Expr *const InitExpr = V->getInit();
+ if (!InitExpr)
+ return; // FIXME: later on we might warn on uninitialized vars too
+
+ const clang::Expr *const InitArgOrigin =
+ tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
+ .first;
+ if (!InitArgOrigin)
+ return;
+
+ if (isa<CXXThisExpr>(InitArgOrigin))
+ return;
+
+ if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
+ if (auto *MaybeGuardian =
+ dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
+ const auto *MaybeGuardianArgType =
+ MaybeGuardian->getType().getTypePtr();
+ if (!MaybeGuardianArgType)
+ return;
+ const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
+ MaybeGuardianArgType->getAsCXXRecordDecl();
+ if (!MaybeGuardianArgCXXRecord)
+ return;
+
+ if (MaybeGuardian->isLocalVarDecl() &&
+ (isRefCounted(MaybeGuardianArgCXXRecord) ||
+ isRefcountedStringsHack(MaybeGuardian)) &&
+ isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
+ return;
+ }
+
+ // Parameters are guaranteed to be safe for the duration of the call
+ // by another checker.
+ if (isa<ParmVarDecl>(MaybeGuardian))
+ return;
+ }
+ }
+
+ reportBug(V);
+ }
+ }
+
+ bool shouldSkipVarDecl(const VarDecl *V) const {
+ assert(V);
+ if (!V->isLocalVarDecl())
+ return true;
+
+ if (isDeclaredInForOrIf(V))
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const VarDecl *V) const {
+ assert(V);
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << "Local variable ";
+ printQuotedQualifiedName(Os, V);
+ Os << " is uncounted and unsafe.";
+
+ PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(V->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedLocalVarsChecker>();
+}
+
+bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 01ac2bc83bb6..8cd7f75e4e38 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -40,7 +40,7 @@ void AnalyzerOptions::printFormattedEntry(
const size_t PadForDesc = InitialPad + EntryWidth;
FOut.PadToColumn(InitialPad) << EntryDescPair.first;
- // If the buffer's length is greater then PadForDesc, print a newline.
+ // If the buffer's length is greater than PadForDesc, print a newline.
if (FOut.getColumn() > PadForDesc)
FOut << '\n';
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 73f057f09550..d1f5ac02278f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -42,7 +42,7 @@ void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID,
}
void PointerToMemberData::Profile(
- llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D,
+ llvm::FoldingSetNodeID &ID, const NamedDecl *D,
llvm::ImmutableList<const CXXBaseSpecifier *> L) {
ID.AddPointer(D);
ID.AddPointer(L.getInternalPointer());
@@ -159,17 +159,17 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
}
const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
- const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+ const NamedDecl *ND, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
llvm::FoldingSetNodeID ID;
- PointerToMemberData::Profile(ID, DD, L);
+ PointerToMemberData::Profile(ID, ND, L);
void *InsertPos;
PointerToMemberData *D =
PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!D) {
- D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>();
- new (D) PointerToMemberData(DD, L);
+ D = (PointerToMemberData *)BPAlloc.Allocate<PointerToMemberData>();
+ new (D) PointerToMemberData(ND, L);
PointerToMemberDataSet.InsertNode(D, InsertPos);
}
@@ -180,25 +180,24 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase(
llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
const nonloc::PointerToMember &PTM) {
nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData();
- const DeclaratorDecl *DD = nullptr;
+ const NamedDecl *ND = nullptr;
llvm::ImmutableList<const CXXBaseSpecifier *> PathList;
- if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) {
- if (PTMDT.is<const DeclaratorDecl *>())
- DD = PTMDT.get<const DeclaratorDecl *>();
+ if (PTMDT.isNull() || PTMDT.is<const NamedDecl *>()) {
+ if (PTMDT.is<const NamedDecl *>())
+ ND = PTMDT.get<const NamedDecl *>();
PathList = CXXBaseListFactory.getEmptyList();
} else { // const PointerToMemberData *
- const PointerToMemberData *PTMD =
- PTMDT.get<const PointerToMemberData *>();
- DD = PTMD->getDeclaratorDecl();
+ const PointerToMemberData *PTMD = PTMDT.get<const PointerToMemberData *>();
+ ND = PTMD->getDeclaratorDecl();
PathList = PTMD->getCXXBaseList();
}
for (const auto &I : llvm::reverse(PathRange))
PathList = prependCXXBase(I, PathList);
- return getPointerToMemberData(DD, PathList);
+ return getPointerToMemberData(ND, PathList);
}
const llvm::APSInt*
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 72be4e81c83d..bf38891b370a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -1570,9 +1570,8 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
if (FID != SM.getFileID(ExpansionRange.getEnd()))
return None;
- bool Invalid;
- const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, &Invalid);
- if (Invalid)
+ Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
+ if (!Buffer)
return None;
unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
@@ -2194,8 +2193,8 @@ void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const {
for (SourceRange range : Ranges) {
if (!range.isValid())
continue;
- hash.AddInteger(range.getBegin().getRawEncoding());
- hash.AddInteger(range.getEnd().getRawEncoding());
+ hash.Add(range.getBegin());
+ hash.Add(range.getEnd());
}
}
@@ -2217,8 +2216,8 @@ void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const {
for (SourceRange range : Ranges) {
if (!range.isValid())
continue;
- hash.AddInteger(range.getBegin().getRawEncoding());
- hash.AddInteger(range.getEnd().getRawEncoding());
+ hash.Add(range.getBegin());
+ hash.Add(range.getEnd());
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index ef4d38ff498f..bc72f4f8c1e3 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -2813,7 +2813,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
//===----------------------------------------------------------------------===//
FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
- : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
+ : Constraints(ConstraintMap::Factory().getEmptyMap()) {}
void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *EndPathNode,
@@ -2855,9 +2855,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
void FalsePositiveRefutationBRVisitor::addConstraints(
const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) {
// Collect new constraints
- const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
- ConstraintRangeTy::Factory &CF =
- N->getState()->get_context<ConstraintRange>();
+ ConstraintMap NewCs = getConstraintMap(N->getState());
+ ConstraintMap::Factory &CF = N->getState()->get_context<ConstraintMap>();
// Add constraints if we don't have them yet
for (auto const &C : NewCs) {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 78d13ddfb773..a55d9302ca58 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -687,7 +687,7 @@ void CXXInstanceCall::getExtraInvalidatedValues(
// base class decl, rather than the class of the instance which needs to be
// checked for mutable fields.
// TODO: We might as well look at the dynamic type of the object.
- const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts();
+ const Expr *Ex = getCXXThisExpr()->IgnoreParenBaseCasts();
QualType T = Ex->getType();
if (T->isPointerType()) // Arrow or implicit-this syntax?
T = T->getPointeeType();
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 725ff1002e29..3d44d2cbc069 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -93,7 +93,7 @@ StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) {
if (Loc.isMacroID())
return Lexer::getImmediateMacroName(Loc, getSourceManager(),
getLangOpts());
- SmallVector<char, 16> buf;
+ SmallString<16> buf;
return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts());
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 9e6d79bb7dcc..ee7474592528 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
@@ -85,6 +86,12 @@ SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
SVal Environment::getSVal(const EnvironmentEntry &Entry,
SValBuilder& svalBuilder) const {
const Stmt *S = Entry.getStmt();
+ assert(!isa<ObjCForCollectionStmt>(S) &&
+ "Use ExprEngine::hasMoreIteration()!");
+ assert((isa<Expr>(S) || isa<ReturnStmt>(S)) &&
+ "Environment can only argue about Exprs, since only they express "
+ "a value! Any non-expression statement stored in Environment is a "
+ "result of a hack!");
const LocationContext *LCtx = Entry.getLocationContext();
switch (S->getStmtClass()) {
@@ -109,6 +116,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::StringLiteralClass:
case Stmt::TypeTraitExprClass:
case Stmt::SizeOfPackExprClass:
+ case Stmt::PredefinedExprClass:
// Known constants; defer to SValBuilder.
return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
@@ -183,18 +191,15 @@ EnvironmentManager::removeDeadBindings(Environment Env,
F.getTreeFactory());
// Iterate over the block-expr bindings.
- for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
+ for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
const SVal &X = I.getData();
- const bool IsBlkExprLive =
- SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
+ const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt());
+ if (!E)
+ continue;
- assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
- "Only Exprs can be live, LivenessAnalysis argues about the liveness "
- "of *values*!");
-
- if (IsBlkExprLive) {
+ if (SymReaper.isLive(E, BlkExpr.getLocationContext())) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 265dcd134213..f285b652c175 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -169,7 +169,7 @@ public:
if (S) {
S->printJson(Out, Helper, PP, /*AddQuotes=*/true);
} else {
- Out << '\"' << I->getAnyMember()->getNameAsString() << '\"';
+ Out << '\"' << I->getAnyMember()->getDeclName() << '\"';
}
}
@@ -2129,6 +2129,83 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
llvm_unreachable("could not resolve condition");
}
+using ObjCForLctxPair =
+ std::pair<const ObjCForCollectionStmt *, const LocationContext *>;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ObjCForHasMoreIterations, ObjCForLctxPair, bool)
+
+ProgramStateRef ExprEngine::setWhetherHasMoreIteration(
+ ProgramStateRef State, const ObjCForCollectionStmt *O,
+ const LocationContext *LC, bool HasMoreIteraton) {
+ assert(!State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return State->set<ObjCForHasMoreIterations>({O, LC}, HasMoreIteraton);
+}
+
+ProgramStateRef
+ExprEngine::removeIterationState(ProgramStateRef State,
+ const ObjCForCollectionStmt *O,
+ const LocationContext *LC) {
+ assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return State->remove<ObjCForHasMoreIterations>({O, LC});
+}
+
+bool ExprEngine::hasMoreIteration(ProgramStateRef State,
+ const ObjCForCollectionStmt *O,
+ const LocationContext *LC) {
+ assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return *State->get<ObjCForHasMoreIterations>({O, LC});
+}
+
+/// Split the state on whether there are any more iterations left for this loop.
+/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the
+/// acquisition of the loop condition value failed.
+static Optional<std::pair<ProgramStateRef, ProgramStateRef>>
+assumeCondition(const Stmt *Condition, ExplodedNode *N) {
+ ProgramStateRef State = N->getState();
+ if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) {
+ bool HasMoreIteraton =
+ ExprEngine::hasMoreIteration(State, ObjCFor, N->getLocationContext());
+ // Checkers have already ran on branch conditions, so the current
+ // information as to whether the loop has more iteration becomes outdated
+ // after this point.
+ State = ExprEngine::removeIterationState(State, ObjCFor,
+ N->getLocationContext());
+ if (HasMoreIteraton)
+ return std::pair<ProgramStateRef, ProgramStateRef>{State, nullptr};
+ else
+ return std::pair<ProgramStateRef, ProgramStateRef>{nullptr, State};
+ }
+ SVal X = State->getSVal(Condition, N->getLocationContext());
+
+ if (X.isUnknownOrUndef()) {
+ // Give it a chance to recover from unknown.
+ if (const auto *Ex = dyn_cast<Expr>(Condition)) {
+ if (Ex->getType()->isIntegralOrEnumerationType()) {
+ // Try to recover some path-sensitivity. Right now casts of symbolic
+ // integers that promote their values are currently not tracked well.
+ // If 'Condition' is such an expression, try and recover the
+ // underlying value and use that instead.
+ SVal recovered =
+ RecoverCastedSymbol(State, Condition, N->getLocationContext(),
+ N->getState()->getStateManager().getContext());
+
+ if (!recovered.isUnknown()) {
+ X = recovered;
+ }
+ }
+ }
+ }
+
+ // If the condition is still unknown, give up.
+ if (X.isUnknownOrUndef())
+ return None;
+
+ DefinedSVal V = X.castAs<DefinedSVal>();
+
+ ProgramStateRef StTrue, StFalse;
+ return State->assume(V);
+}
+
void ExprEngine::processBranch(const Stmt *Condition,
NodeBuilderContext& BldCtx,
ExplodedNode *Pred,
@@ -2165,48 +2242,28 @@ void ExprEngine::processBranch(const Stmt *Condition,
return;
BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
- for (const auto PredI : CheckersOutSet) {
- if (PredI->isSink())
+ for (ExplodedNode *PredN : CheckersOutSet) {
+ if (PredN->isSink())
continue;
- ProgramStateRef PrevState = PredI->getState();
- SVal X = PrevState->getSVal(Condition, PredI->getLocationContext());
-
- if (X.isUnknownOrUndef()) {
- // Give it a chance to recover from unknown.
- if (const auto *Ex = dyn_cast<Expr>(Condition)) {
- if (Ex->getType()->isIntegralOrEnumerationType()) {
- // Try to recover some path-sensitivity. Right now casts of symbolic
- // integers that promote their values are currently not tracked well.
- // If 'Condition' is such an expression, try and recover the
- // underlying value and use that instead.
- SVal recovered = RecoverCastedSymbol(PrevState, Condition,
- PredI->getLocationContext(),
- getContext());
-
- if (!recovered.isUnknown()) {
- X = recovered;
- }
- }
- }
- }
+ ProgramStateRef PrevState = PredN->getState();
- // If the condition is still unknown, give up.
- if (X.isUnknownOrUndef()) {
- builder.generateNode(PrevState, true, PredI);
- builder.generateNode(PrevState, false, PredI);
+ ProgramStateRef StTrue, StFalse;
+ if (const auto KnownCondValueAssumption = assumeCondition(Condition, PredN))
+ std::tie(StTrue, StFalse) = *KnownCondValueAssumption;
+ else {
+ assert(!isa<ObjCForCollectionStmt>(Condition));
+ builder.generateNode(PrevState, true, PredN);
+ builder.generateNode(PrevState, false, PredN);
continue;
}
-
- DefinedSVal V = X.castAs<DefinedSVal>();
-
- ProgramStateRef StTrue, StFalse;
- std::tie(StTrue, StFalse) = PrevState->assume(V);
+ if (StTrue && StFalse)
+ assert(!isa<ObjCForCollectionStmt>(Condition));;
// Process the true branch.
if (builder.isFeasible(true)) {
if (StTrue)
- builder.generateNode(StTrue, true, PredI);
+ builder.generateNode(StTrue, true, PredN);
else
builder.markInfeasible(true);
}
@@ -2214,7 +2271,7 @@ void ExprEngine::processBranch(const Stmt *Condition,
// Process the false branch.
if (builder.isFeasible(false)) {
if (StFalse)
- builder.generateNode(StFalse, false, PredI);
+ builder.generateNode(StFalse, false, PredN);
else
builder.markInfeasible(false);
}
@@ -2530,16 +2587,8 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
- // FIXME: Compute lvalue of field pointers-to-member.
- // Right now we just use a non-null void pointer, so that it gives proper
- // results in boolean contexts.
- // FIXME: Maybe delegate this to the surrounding operator&.
- // Note how this expression is lvalue, however pointer-to-member is NonLoc.
- SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy,
- currBldrCtx->blockCount());
- state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true);
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ // Delegate all work related to pointer to members to the surrounding
+ // operator&.
return;
}
if (isa<BindingDecl>(D)) {
@@ -3100,7 +3149,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
if (Stop(N))
return true;
- if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc()))
+ if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc(), nullptr))
break;
PostCallback(N);
@@ -3109,7 +3158,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
return false;
}
- static bool isNodeHidden(const ExplodedNode *N) {
+ static bool isNodeHidden(const ExplodedNode *N, const ExplodedGraph *G) {
return N->isTrivial();
}
@@ -3162,8 +3211,9 @@ void ExprEngine::ViewGraph(bool trim) {
#ifndef NDEBUG
std::string Filename = DumpGraph(trim);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#endif
+#else
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+#endif
}
@@ -3171,8 +3221,9 @@ void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
#ifndef NDEBUG
std::string Filename = DumpGraph(Nodes);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#endif
+#else
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+#endif
}
std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
@@ -3209,15 +3260,17 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
if (!TrimmedG.get()) {
llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
+ return "";
} else {
return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
/*ShortNames=*/false,
/*Title=*/"Trimmed Exploded Graph",
/*Filename=*/std::string(Filename));
}
-#endif
+#else
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
return "";
+#endif
}
void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c5e38cc7423d..18d1b2169eed 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -418,6 +418,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_ZeroToOCLOpaqueType:
case CK_IntToOCLSampler:
case CK_LValueBitCast:
+ case CK_FloatingToFixedPoint:
+ case CK_FixedPointToFloating:
case CK_FixedPointCast:
case CK_FixedPointToBoolean:
case CK_FixedPointToIntegral:
@@ -991,10 +993,11 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) {
const ValueDecl *VD = DRE->getDecl();
- if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) {
+ if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) ||
+ isa<IndirectFieldDecl>(VD)) {
ProgramStateRef State = (*I)->getState();
const LocationContext *LCtx = (*I)->getLocationContext();
- SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD));
+ SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV));
break;
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 38a680eb04c0..cab65687444b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -132,10 +132,20 @@ SVal ExprEngine::computeObjectUnderConstruction(
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
const auto *Init = ICC->getCXXCtorInitializer();
- assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
+ if (Init->isBaseInitializer()) {
+ const auto *ThisReg = cast<SubRegion>(ThisVal.getAsRegion());
+ const CXXRecordDecl *BaseClass =
+ Init->getBaseClass()->getAsCXXRecordDecl();
+ const auto *BaseReg =
+ MRMgr.getCXXBaseObjectRegion(BaseClass, ThisReg,
+ Init->isBaseVirtual());
+ return SVB.makeLoc(BaseReg);
+ }
+ if (Init->isDelegatingInitializer())
+ return ThisVal;
const ValueDecl *Field;
SVal FieldVal;
@@ -364,8 +374,12 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
- return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(),
- LCtx, V);
+ const auto *Init = ICC->getCXXCtorInitializer();
+ // Base and delegating initializers handled above
+ assert(Init->isAnyMemberInitializer() &&
+ "Base and delegating initializers should have been handled by"
+ "computeObjectUnderConstruction()");
+ return addObjectUnderConstruction(State, Init, LCtx, V);
}
case ConstructionContext::NewAllocatedObjectKind: {
return State;
@@ -602,11 +616,11 @@ void ExprEngine::handleConstructor(const Expr *E,
*Call, *this);
ExplodedNodeSet DstEvaluated;
- StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
if (CE && CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
!CallOpts.IsArrayCtorOrDtor) {
+ StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
// FIXME: Handle other kinds of trivial constructors as well.
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
@@ -626,6 +640,8 @@ void ExprEngine::handleConstructor(const Expr *E,
// in the CFG, would be called at the end of the full expression or
// later (for life-time extended temporaries) -- but avoids infeasible
// paths when no-return temporary destructors are used for assertions.
+ ExplodedNodeSet DstEvaluatedPostProcessed;
+ StmtNodeBuilder Bldr(DstEvaluated, DstEvaluatedPostProcessed, *currBldrCtx);
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
@@ -655,7 +671,7 @@ void ExprEngine::handleConstructor(const Expr *E,
}
ExplodedNodeSet DstPostArgumentCleanup;
- for (ExplodedNode *I : DstEvaluated)
+ for (ExplodedNode *I : DstEvaluatedPostProcessed)
finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
// If there were other constructors called for object-type arguments
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 52ba17d59ae0..996d3644e018 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -842,19 +842,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD,
StringRef Name) {
const IdentifierInfo &II = Ctx.Idents.get(Name);
- DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II);
- if (!RD->lookup(DeclName).empty())
- return true;
-
- CXXBasePaths Paths(false, false, false);
- if (RD->lookupInBases(
- [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
- return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName);
- },
- Paths))
- return true;
-
- return false;
+ return RD->hasMemberName(Ctx.DeclarationNames.getIdentifier(&II));
}
/// Returns true if the given C++ class is a container or iterator.
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index eb9a0be2e5d6..5a55e81497b0 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -53,10 +53,8 @@ static void populateObjCForDestinationSet(
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
- SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
-
- // FIXME: S is not an expression. We should not be binding values to it.
- ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
+ ProgramStateRef nextState =
+ ExprEngine::setWhetherHasMoreIteration(state, S, LCtx, hasElements);
if (auto MV = elementV.getAs<loc::MemRegionVal>())
if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
@@ -93,10 +91,9 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
// (1) binds the next container value to 'element'. This creates a new
// node in the ExplodedGraph.
//
- // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
- // whether or not the container has any more elements. This value
- // will be tested in ProcessBranch. We need to explicitly bind
- // this value because a container can contain nil elements.
+ // (2) note whether the collection has any more elements (or in other words,
+ // whether the loop has more iterations). This will be tested in
+ // processBranch.
//
// FIXME: Eventually this logic should actually do dispatches to
// 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index bc7c41d039c4..149459cf986a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -23,8 +24,6 @@
#include "clang/Lex/Token.h"
#include "clang/Rewrite/Core/HTMLRewrite.h"
#include "clang/Rewrite/Core/Rewriter.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
@@ -58,17 +57,18 @@ using namespace ento;
namespace {
class HTMLDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
std::string Directory;
bool createdDir = false;
bool noDir = false;
const Preprocessor &PP;
- AnalyzerOptions &AnalyzerOpts;
const bool SupportsCrossFileDiagnostics;
public:
- HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &OutputDir,
- const Preprocessor &pp, bool supportsMultipleFiles)
- : Directory(OutputDir), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
+ const std::string &OutputDir, const Preprocessor &pp,
+ bool supportsMultipleFiles)
+ : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
@@ -133,7 +133,7 @@ private:
} // namespace
void ento::createHTMLDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &OutputDir, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
@@ -142,37 +142,38 @@ void ento::createHTMLDiagnosticConsumer(
// output mode. This doesn't make much sense, we should have the minimal text
// as our default. In the case of backward compatibility concerns, this could
// be preserved with -analyzer-config-compatibility-mode=true.
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU);
// TODO: Emit an error here.
if (OutputDir.empty())
return;
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, true));
+ C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true));
}
void ento::createHTMLSingleFileDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &OutputDir, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
+ createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU);
// TODO: Emit an error here.
if (OutputDir.empty())
return;
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, false));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+ C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false));
}
void ento::createPlistHTMLDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &prefix, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
createHTMLDiagnosticConsumer(
- AnalyzerOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
+ DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
CTU);
- createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
+ createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP,
+ CTU);
}
//===----------------------------------------------------------------------===//
@@ -245,7 +246,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
int FD;
SmallString<128> Model, ResultPath;
- if (!AnalyzerOpts.ShouldWriteStableReportFilename) {
+ if (!DiagOpts.ShouldWriteStableReportFilename) {
llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
if (std::error_code EC =
llvm::sys::fs::make_absolute(Model)) {
@@ -535,7 +536,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<input type="checkbox" class="spoilerhider" id="showinvocation" />
<label for="showinvocation" >Show analyzer invocation</label>
<div class="spoiler">clang -cc1 )<<<";
- os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation);
+ os << html::EscapeText(DiagOpts.ToolInvocation);
os << R"<<<(
</div>
<div id='tooltiphint' hidden="true">
@@ -582,8 +583,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
- << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(),
- DeclWithIssue, PP.getLangOpts())
+ << getIssueHash(L, D.getCheckerName(), D.getBugType(), DeclWithIssue,
+ PP.getLangOpts())
<< " -->\n";
os << "\n<!-- BUGLINE "
@@ -786,8 +787,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
if (LPosInfo.first != BugFileID)
return;
- const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first);
- const char* FileStart = Buf->getBufferStart();
+ llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first);
+ const char *FileStart = Buf.getBufferStart();
// Compute the column number. Rewind from the current position to the start
// of the line.
@@ -797,7 +798,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
// Compute LineEnd.
const char *LineEnd = TokInstantiationPtr;
- const char* FileEnd = Buf->getBufferEnd();
+ const char *FileEnd = Buf.getBufferEnd();
while (*LineEnd != '\n' && LineEnd != FileEnd)
++LineEnd;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
deleted file mode 100644
index e7497f3fbdaa..000000000000
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/Specifiers.h"
-#include "clang/Lex/Lexer.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/LineIterator.h"
-#include "llvm/Support/MD5.h"
-#include "llvm/Support/Path.h"
-
-#include <functional>
-#include <sstream>
-#include <string>
-
-using namespace clang;
-
-// Get a string representation of the parts of the signature that can be
-// overloaded on.
-static std::string GetSignature(const FunctionDecl *Target) {
- if (!Target)
- return "";
- std::string Signature;
-
- // When a flow sensitive bug happens in templated code we should not generate
- // distinct hash value for every instantiation. Use the signature from the
- // primary template.
- if (const FunctionDecl *InstantiatedFrom =
- Target->getTemplateInstantiationPattern())
- Target = InstantiatedFrom;
-
- if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
- !isa<CXXConversionDecl>(Target))
- Signature.append(Target->getReturnType().getAsString()).append(" ");
- Signature.append(Target->getQualifiedNameAsString()).append("(");
-
- for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
- if (i)
- Signature.append(", ");
- Signature.append(Target->getParamDecl(i)->getType().getAsString());
- }
-
- if (Target->isVariadic())
- Signature.append(", ...");
- Signature.append(")");
-
- const auto *TargetT =
- llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
-
- if (!TargetT || !isa<CXXMethodDecl>(Target))
- return Signature;
-
- if (TargetT->isConst())
- Signature.append(" const");
- if (TargetT->isVolatile())
- Signature.append(" volatile");
- if (TargetT->isRestrict())
- Signature.append(" restrict");
-
- if (const auto *TargetPT =
- dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
- switch (TargetPT->getRefQualifier()) {
- case RQ_LValue:
- Signature.append(" &");
- break;
- case RQ_RValue:
- Signature.append(" &&");
- break;
- default:
- break;
- }
- }
-
- return Signature;
-}
-
-static std::string GetEnclosingDeclContextSignature(const Decl *D) {
- if (!D)
- return "";
-
- if (const auto *ND = dyn_cast<NamedDecl>(D)) {
- std::string DeclName;
-
- switch (ND->getKind()) {
- case Decl::Namespace:
- case Decl::Record:
- case Decl::CXXRecord:
- case Decl::Enum:
- DeclName = ND->getQualifiedNameAsString();
- break;
- case Decl::CXXConstructor:
- case Decl::CXXDestructor:
- case Decl::CXXConversion:
- case Decl::CXXMethod:
- case Decl::Function:
- DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
- break;
- case Decl::ObjCMethod:
- // ObjC Methods can not be overloaded, qualified name uniquely identifies
- // the method.
- DeclName = ND->getQualifiedNameAsString();
- break;
- default:
- break;
- }
-
- return DeclName;
- }
-
- return "";
-}
-
-static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) {
- if (!Buffer)
- return "";
-
- llvm::line_iterator LI(*Buffer, false);
- for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
- ;
-
- return *LI;
-}
-
-static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
- const LangOptions &LangOpts) {
- static StringRef Whitespaces = " \t\n";
-
- StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
- L.getExpansionLineNumber());
- StringRef::size_type col = Str.find_first_not_of(Whitespaces);
- if (col == StringRef::npos)
- col = 1; // The line only contains whitespace.
- else
- col++;
- SourceLocation StartOfLine =
- SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
- const llvm::MemoryBuffer *Buffer =
- SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
- if (!Buffer)
- return {};
-
- const char *BufferPos = SM.getCharacterData(StartOfLine);
-
- Token Token;
- Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
- Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
-
- size_t NextStart = 0;
- std::ostringstream LineBuff;
- while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
- if (Token.isAtStartOfLine() && NextStart++ > 0)
- continue;
- LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
- Token.getLength());
- }
-
- return LineBuff.str();
-}
-
-static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
- llvm::MD5 Hash;
- llvm::MD5::MD5Result MD5Res;
- SmallString<32> Res;
-
- Hash.update(Content);
- Hash.final(MD5Res);
- llvm::MD5::stringifyResult(MD5Res, Res);
-
- return Res;
-}
-
-std::string clang::GetIssueString(const SourceManager &SM,
- FullSourceLoc &IssueLoc,
- StringRef CheckerName, StringRef BugType,
- const Decl *D,
- const LangOptions &LangOpts) {
- static StringRef Delimiter = "$";
-
- return (llvm::Twine(CheckerName) + Delimiter +
- GetEnclosingDeclContextSignature(D) + Delimiter +
- Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
- NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
- .str();
-}
-
-SmallString<32> clang::GetIssueHash(const SourceManager &SM,
- FullSourceLoc &IssueLoc,
- StringRef CheckerName, StringRef BugType,
- const Decl *D,
- const LangOptions &LangOpts) {
-
- return GetHashOfContent(
- GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));
-}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index ed62778623a8..35e320c7755f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/PlistSupport.h"
@@ -20,13 +21,12 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/TokenConcatenation.h"
#include "clang/Rewrite/Core/HTMLRewrite.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
+#include <memory>
using namespace clang;
using namespace ento;
@@ -39,13 +39,17 @@ using namespace markup;
namespace {
class PlistDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
const std::string OutputFile;
const Preprocessor &PP;
const cross_tu::CrossTranslationUnitContext &CTU;
- AnalyzerOptions &AnOpts;
const bool SupportsCrossFileDiagnostics;
+
+ void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
+ const PathPieces &Path);
+
public:
- PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU,
bool supportsMultipleFiles);
@@ -74,23 +78,19 @@ namespace {
/// A helper class for emitting a single report.
class PlistPrinter {
const FIDMap& FM;
- AnalyzerOptions &AnOpts;
const Preprocessor &PP;
const cross_tu::CrossTranslationUnitContext &CTU;
llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
public:
- PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts,
+ PlistPrinter(const FIDMap& FM,
const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU)
- : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) {
+ : FM(FM), PP(PP), CTU(CTU) {
}
void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
-
- // Don't emit a warning about an unused private field.
- (void)AnOpts;
}
/// Print the expansions of the collected macro pieces.
@@ -165,11 +165,6 @@ struct ExpansionInfo {
} // end of anonymous namespace
-static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU,
- const PathPieces &Path);
-
/// Print coverage information to output stream {@code o}.
/// May modify the used list of files {@code Fids} by inserting new ones.
static void printCoverage(const PathDiagnostic *D,
@@ -520,11 +515,53 @@ static void printCoverage(const PathDiagnostic *D,
assert(IndentLevel == InputIndentLevel);
}
-static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU,
- const PathPieces &Path) {
- PlistPrinter Printer(FM, AnOpts, PP, CTU);
+//===----------------------------------------------------------------------===//
+// Methods of PlistDiagnostics.
+//===----------------------------------------------------------------------===//
+
+PlistDiagnostics::PlistDiagnostics(
+ PathDiagnosticConsumerOptions DiagOpts, const std::string &output,
+ const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
+ bool supportsMultipleFiles)
+ : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {
+ // FIXME: Will be used by a later planned change.
+ (void)this->CTU;
+}
+
+void ento::createPlistDiagnosticConsumer(
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputFile, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
+ /*supportsMultipleFiles=*/false));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
+ PP, CTU);
+}
+
+void ento::createPlistMultiFileDiagnosticConsumer(
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputFile, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
+ /*supportsMultipleFiles=*/true));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
+ PP, CTU);
+}
+
+void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
+ const PathPieces &Path) {
+ PlistPrinter Printer(FM, PP, CTU);
assert(std::is_partitioned(Path.begin(), Path.end(),
[](const PathDiagnosticPieceRef &E) {
return E->getKind() == PathDiagnosticPiece::Note;
@@ -557,7 +594,7 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
o << " </array>\n";
- if (!AnOpts.ShouldDisplayMacroExpansions)
+ if (!DiagOpts.ShouldDisplayMacroExpansions)
return;
o << " <key>macro_expansions</key>\n"
@@ -566,48 +603,6 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
o << " </array>\n";
}
-//===----------------------------------------------------------------------===//
-// Methods of PlistDiagnostics.
-//===----------------------------------------------------------------------===//
-
-PlistDiagnostics::PlistDiagnostics(
- AnalyzerOptions &AnalyzerOpts, const std::string &output,
- const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
- bool supportsMultipleFiles)
- : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {
- // FIXME: Will be used by a later planned change.
- (void)this->CTU;
-}
-
-void ento::createPlistDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &OutputFile, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
-
- // TODO: Emit an error here.
- if (OutputFile.empty())
- return;
-
- C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
- /*supportsMultipleFiles*/ false));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
-}
-
-void ento::createPlistMultiFileDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &OutputFile, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
-
- // TODO: Emit an error here.
- if (OutputFile.empty())
- return;
-
- C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
- /*supportsMultipleFiles*/ true));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
-}
-
void PlistDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) {
@@ -682,7 +677,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <dict>\n";
const PathDiagnostic *D = *DI;
- printBugPath(o, FM, AnOpts, PP, CTU, D->path);
+ printBugPath(o, FM, D->path);
// Output the bug type and bug category.
o << " <key>description</key>";
@@ -702,7 +697,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
: D->getLocation().asLocation()),
SM);
const Decl *DeclWithIssue = D->getDeclWithIssue();
- EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(),
+ EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(),
DeclWithIssue, LangOpts))
<< '\n';
@@ -806,7 +801,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n';
o << " </array>\n";
- if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) {
+ if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) {
o << " <key>statistics</key>\n";
std::string stats;
llvm::raw_string_ostream os(stats);
@@ -825,22 +820,36 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
namespace {
-using ExpArgTokens = llvm::SmallVector<Token, 2>;
+using ArgTokensTy = llvm::SmallVector<Token, 2>;
+
+} // end of anonymous namespace
+
+LLVM_DUMP_METHOD static void dumpArgTokensToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP,
+ const ArgTokensTy &Toks);
-/// Maps unexpanded macro arguments to expanded arguments. A macro argument may
+namespace {
+/// Maps unexpanded macro parameters to expanded arguments. A macro argument may
/// need to expanded further when it is nested inside another macro.
-class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> {
+class MacroParamMap : public std::map<const IdentifierInfo *, ArgTokensTy> {
public:
- void expandFromPrevMacro(const MacroArgMap &Super);
+ void expandFromPrevMacro(const MacroParamMap &Super);
+
+ LLVM_DUMP_METHOD void dump(const Preprocessor &PP) const {
+ dumpToStream(llvm::errs(), PP);
+ }
+
+ LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP) const;
};
-struct MacroNameAndArgs {
+struct MacroExpansionInfo {
std::string Name;
const MacroInfo *MI = nullptr;
- MacroArgMap Args;
+ MacroParamMap ParamMap;
- MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M)
- : Name(std::move(N)), MI(MI), Args(std::move(M)) {}
+ MacroExpansionInfo(std::string N, const MacroInfo *MI, MacroParamMap M)
+ : Name(std::move(N)), MI(MI), ParamMap(std::move(M)) {}
};
class TokenPrinter {
@@ -860,6 +869,46 @@ public:
void printToken(const Token &Tok);
};
+/// Wrapper around a Lexer object that can lex tokens one-by-one. Its possible
+/// to "inject" a range of tokens into the stream, in which case the next token
+/// is retrieved from the next element of the range, until the end of the range
+/// is reached.
+class TokenStream {
+public:
+ TokenStream(SourceLocation ExpanLoc, const SourceManager &SM,
+ const LangOptions &LangOpts)
+ : ExpanLoc(ExpanLoc) {
+ FileID File;
+ unsigned Offset;
+ std::tie(File, Offset) = SM.getDecomposedLoc(ExpanLoc);
+ llvm::MemoryBufferRef MB = SM.getBufferOrFake(File);
+ const char *MacroNameTokenPos = MB.getBufferStart() + Offset;
+
+ RawLexer = std::make_unique<Lexer>(SM.getLocForStartOfFile(File), LangOpts,
+ MB.getBufferStart(), MacroNameTokenPos,
+ MB.getBufferEnd());
+ }
+
+ void next(Token &Result) {
+ if (CurrTokenIt == TokenRange.end()) {
+ RawLexer->LexFromRawLexer(Result);
+ return;
+ }
+ Result = *CurrTokenIt;
+ CurrTokenIt++;
+ }
+
+ void injectRange(const ArgTokensTy &Range) {
+ TokenRange = Range;
+ CurrTokenIt = TokenRange.begin();
+ }
+
+ std::unique_ptr<Lexer> RawLexer;
+ ArgTokensTy TokenRange;
+ ArgTokensTy::iterator CurrTokenIt = TokenRange.begin();
+ SourceLocation ExpanLoc;
+};
+
} // end of anonymous namespace
/// The implementation method of getMacroExpansion: It prints the expansion of
@@ -878,7 +927,7 @@ public:
///
/// As we expand the last line, we'll immediately replace PRINT(str) with
/// print(x). The information that both 'str' and 'x' refers to the same string
-/// is an information we have to forward, hence the argument \p PrevArgs.
+/// is an information we have to forward, hence the argument \p PrevParamMap.
///
/// To avoid infinite recursion we maintain the already processed tokens in
/// a set. This is carried as a parameter through the recursive calls. The set
@@ -888,13 +937,11 @@ public:
/// #define f(y) x
/// #define x f(x)
static std::string getMacroNameAndPrintExpansion(
- TokenPrinter &Printer,
- SourceLocation MacroLoc,
- const Preprocessor &PP,
- const MacroArgMap &PrevArgs,
+ TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
+ const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens);
-/// Retrieves the name of the macro and what it's arguments expand into
+/// Retrieves the name of the macro and what it's parameters expand into
/// at \p ExpanLoc.
///
/// For example, for the following macro expansion:
@@ -916,8 +963,9 @@ static std::string getMacroNameAndPrintExpansion(
/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of
/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map
/// { (x, a) } will be returned.
-static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
- const Preprocessor &PP);
+static MacroExpansionInfo
+getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
+ SourceLocation ExpanLoc, const Preprocessor &PP);
/// Retrieves the ')' token that matches '(' \p It points to.
static MacroInfo::tokens_iterator getMatchingRParen(
@@ -951,21 +999,20 @@ getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens;
std::string MacroName = getMacroNameAndPrintExpansion(
- Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens);
+ Printer, MacroLoc, *PPToUse, MacroParamMap{}, AlreadyProcessedTokens);
return {MacroName, std::string(OS.str())};
}
static std::string getMacroNameAndPrintExpansion(
- TokenPrinter &Printer,
- SourceLocation MacroLoc,
- const Preprocessor &PP,
- const MacroArgMap &PrevArgs,
+ TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
+ const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) {
const SourceManager &SM = PP.getSourceManager();
- MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP);
- IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name);
+ MacroExpansionInfo MExpInfo =
+ getMacroExpansionInfo(PrevParamMap, SM.getExpansionLoc(MacroLoc), PP);
+ IdentifierInfo *MacroNameII = PP.getIdentifierInfo(MExpInfo.Name);
// TODO: If the macro definition contains another symbol then this function is
// called recursively. In case this symbol is the one being defined, it will
@@ -973,18 +1020,18 @@ static std::string getMacroNameAndPrintExpansion(
// in this case we don't get the full expansion text in the Plist file. See
// the test file where "value" is expanded to "garbage_" instead of
// "garbage_value".
- if (!AlreadyProcessedTokens.insert(IDInfo).second)
- return Info.Name;
+ if (!AlreadyProcessedTokens.insert(MacroNameII).second)
+ return MExpInfo.Name;
- if (!Info.MI)
- return Info.Name;
+ if (!MExpInfo.MI)
+ return MExpInfo.Name;
// Manually expand its arguments from the previous macro.
- Info.Args.expandFromPrevMacro(PrevArgs);
+ MExpInfo.ParamMap.expandFromPrevMacro(PrevParamMap);
// Iterate over the macro's tokens and stringify them.
- for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E;
- ++It) {
+ for (auto It = MExpInfo.MI->tokens_begin(), E = MExpInfo.MI->tokens_end();
+ It != E; ++It) {
Token T = *It;
// If this token is not an identifier, we only need to print it.
@@ -1000,8 +1047,8 @@ static std::string getMacroNameAndPrintExpansion(
// If this token is a macro that should be expanded inside the current
// macro.
if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) {
- getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args,
- AlreadyProcessedTokens);
+ getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP,
+ MExpInfo.ParamMap, AlreadyProcessedTokens);
// If this is a function-like macro, skip its arguments, as
// getExpandedMacro() already printed them. If this is the case, let's
@@ -1013,10 +1060,10 @@ static std::string getMacroNameAndPrintExpansion(
}
// If this token is the current macro's argument, we should expand it.
- auto ArgMapIt = Info.Args.find(II);
- if (ArgMapIt != Info.Args.end()) {
- for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(),
- ArgEnd = ArgMapIt->second.end();
+ auto ParamToArgIt = MExpInfo.ParamMap.find(II);
+ if (ParamToArgIt != MExpInfo.ParamMap.end()) {
+ for (MacroInfo::tokens_iterator ArgIt = ParamToArgIt->second.begin(),
+ ArgEnd = ParamToArgIt->second.end();
ArgIt != ArgEnd; ++ArgIt) {
// These tokens may still be macros, if that is the case, handle it the
@@ -1034,7 +1081,8 @@ static std::string getMacroNameAndPrintExpansion(
}
getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP,
- Info.Args, AlreadyProcessedTokens);
+ MExpInfo.ParamMap,
+ AlreadyProcessedTokens);
// Peek the next token if it is a tok::l_paren. This way we can decide
// if this is the application or just a reference to a function maxro
// symbol:
@@ -1055,34 +1103,30 @@ static std::string getMacroNameAndPrintExpansion(
Printer.printToken(T);
}
- AlreadyProcessedTokens.erase(IDInfo);
+ AlreadyProcessedTokens.erase(MacroNameII);
- return Info.Name;
+ return MExpInfo.Name;
}
-static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
- const Preprocessor &PP) {
+static MacroExpansionInfo
+getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
+ SourceLocation ExpanLoc, const Preprocessor &PP) {
const SourceManager &SM = PP.getSourceManager();
const LangOptions &LangOpts = PP.getLangOpts();
// First, we create a Lexer to lex *at the expansion location* the tokens
// referring to the macro's name and its arguments.
- std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc);
- const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first);
- const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second;
-
- Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts,
- MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd());
+ TokenStream TStream(ExpanLoc, SM, LangOpts);
// Acquire the macro's name.
Token TheTok;
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
std::string MacroName = PP.getSpelling(TheTok);
const auto *II = PP.getIdentifierInfo(MacroName);
- assert(II && "Failed to acquire the IndetifierInfo for the macro!");
+ assert(II && "Failed to acquire the IdentifierInfo for the macro!");
const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc);
// assert(MI && "The macro must've been defined at it's expansion location!");
@@ -1094,18 +1138,18 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (!MI)
return { MacroName, MI, {} };
- // Acquire the macro's arguments.
+ // Acquire the macro's arguments at the expansion point.
//
// The rough idea here is to lex from the first left parentheses to the last
- // right parentheses, and map the macro's unexpanded arguments to what they
- // will be expanded to. An expanded macro argument may contain several tokens
- // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at
- // which point we start lexing the next argument or finish.
- ArrayRef<const IdentifierInfo *> MacroArgs = MI->params();
- if (MacroArgs.empty())
+ // right parentheses, and map the macro's parameter to what they will be
+ // expanded to. A macro argument may contain several token (like '3 + 4'), so
+ // we'll lex until we find a tok::comma or tok::r_paren, at which point we
+ // start lexing the next argument or finish.
+ ArrayRef<const IdentifierInfo *> MacroParams = MI->params();
+ if (MacroParams.empty())
return { MacroName, MI, {} };
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
// When this is a token which expands to another macro function then its
// parentheses are not at its expansion locaiton. For example:
//
@@ -1117,9 +1161,9 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (TheTok.isNot(tok::l_paren))
return { MacroName, MI, {} };
- MacroArgMap Args;
+ MacroParamMap ParamMap;
- // When the macro's argument is a function call, like
+ // When the argument is a function call, like
// CALL_FN(someFunctionName(param1, param2))
// we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide
// actual macro arguments, or do not represent the macro argument's closing
@@ -1130,12 +1174,19 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
// * > 1, then tok::comma is a part of the current arg.
int ParenthesesDepth = 1;
- // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren,
- // even if we lex a tok::comma and ParanthesesDepth == 1.
- const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__");
+ // If we encounter the variadic arg, we will lex until the closing
+ // tok::r_paren, even if we lex a tok::comma and ParanthesesDepth == 1.
+ const IdentifierInfo *VariadicParamII = PP.getIdentifierInfo("__VA_ARGS__");
+ if (MI->isGNUVarargs()) {
+ // If macro uses GNU-style variadic args, the param name is user-supplied,
+ // an not "__VA_ARGS__". E.g.:
+ // #define FOO(a, b, myvargs...)
+ // In this case, just use the last parameter:
+ VariadicParamII = *(MacroParams.rbegin());
+ }
- for (const IdentifierInfo *UnexpArgII : MacroArgs) {
- MacroArgMap::mapped_type ExpandedArgTokens;
+ for (const IdentifierInfo *CurrParamII : MacroParams) {
+ MacroParamMap::mapped_type ArgTokens;
// One could also simply not supply a single argument to __VA_ARGS__ -- this
// results in a preprocessor warning, but is not an error:
@@ -1149,10 +1200,10 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (ParenthesesDepth != 0) {
// Lex the first token of the next macro parameter.
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
- while (!(ParenthesesDepth == 1 &&
- (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) {
+ while (CurrParamII == VariadicParamII || ParenthesesDepth != 1 ||
+ !TheTok.is(tok::comma)) {
assert(TheTok.isNot(tok::eof) &&
"EOF encountered while looking for expanded macro args!");
@@ -1165,24 +1216,51 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (ParenthesesDepth == 0)
break;
- if (TheTok.is(tok::raw_identifier))
+ if (TheTok.is(tok::raw_identifier)) {
PP.LookUpIdentifierInfo(TheTok);
+ // This token is a variadic parameter:
+ //
+ // #define PARAMS_RESOLVE_TO_VA_ARGS(i, fmt) foo(i, fmt); \
+ // i = 0;
+ // #define DISPATCH(...) \
+ // PARAMS_RESOLVE_TO_VA_ARGS(__VA_ARGS__);
+ // // ^~~~~~~~~~~ Variadic parameter here
+ //
+ // void multipleParamsResolveToVA_ARGS(void) {
+ // int x = 1;
+ // DISPATCH(x, "LF1M healer"); // Multiple arguments are mapped to
+ // // a single __VA_ARGS__ parameter.
+ // (void)(10 / x);
+ // }
+ //
+ // We will stumble across this while trying to expand
+ // PARAMS_RESOLVE_TO_VA_ARGS. By this point, we already noted during
+ // the processing of DISPATCH what __VA_ARGS__ maps to, so we'll
+ // retrieve the next series of tokens from that.
+ if (TheTok.getIdentifierInfo() == VariadicParamII) {
+ TStream.injectRange(PrevParamMap.at(VariadicParamII));
+ TStream.next(TheTok);
+ continue;
+ }
+ }
- ExpandedArgTokens.push_back(TheTok);
- RawLexer.LexFromRawLexer(TheTok);
+ ArgTokens.push_back(TheTok);
+ TStream.next(TheTok);
}
} else {
- assert(UnexpArgII == __VA_ARGS__II);
+ assert(CurrParamII == VariadicParamII &&
+ "No more macro arguments are found, but the current parameter "
+ "isn't the variadic arg!");
}
- Args.emplace(UnexpArgII, std::move(ExpandedArgTokens));
+ ParamMap.emplace(CurrParamII, std::move(ArgTokens));
}
assert(TheTok.is(tok::r_paren) &&
"Expanded macro argument acquisition failed! After the end of the loop"
" this token should be ')'!");
- return { MacroName, MI, Args };
+ return {MacroName, MI, ParamMap};
}
static MacroInfo::tokens_iterator getMatchingRParen(
@@ -1222,14 +1300,14 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo();
}
-void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
+void MacroParamMap::expandFromPrevMacro(const MacroParamMap &Super) {
for (value_type &Pair : *this) {
- ExpArgTokens &CurrExpArgTokens = Pair.second;
+ ArgTokensTy &CurrArgTokens = Pair.second;
// For each token in the expanded macro argument.
- auto It = CurrExpArgTokens.begin();
- while (It != CurrExpArgTokens.end()) {
+ auto It = CurrArgTokens.begin();
+ while (It != CurrArgTokens.end()) {
if (It->isNot(tok::identifier)) {
++It;
continue;
@@ -1244,17 +1322,43 @@ void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
continue;
}
- const ExpArgTokens &SuperExpArgTokens = Super.at(II);
+ const ArgTokensTy &SuperArgTokens = Super.at(II);
- It = CurrExpArgTokens.insert(
- It, SuperExpArgTokens.begin(), SuperExpArgTokens.end());
- std::advance(It, SuperExpArgTokens.size());
- It = CurrExpArgTokens.erase(It);
+ It = CurrArgTokens.insert(It, SuperArgTokens.begin(),
+ SuperArgTokens.end());
+ std::advance(It, SuperArgTokens.size());
+ It = CurrArgTokens.erase(It);
}
}
}
+void MacroParamMap::dumpToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP) const {
+ for (const std::pair<const IdentifierInfo *, ArgTokensTy> Pair : *this) {
+ Out << Pair.first->getName() << " -> ";
+ dumpArgTokensToStream(Out, PP, Pair.second);
+ Out << '\n';
+ }
+}
+
+static void dumpArgTokensToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP,
+ const ArgTokensTy &Toks) {
+ TokenPrinter Printer(Out, PP);
+ for (Token Tok : Toks)
+ Printer.printToken(Tok);
+}
+
void TokenPrinter::printToken(const Token &Tok) {
+ // TODO: Handle GNU extensions where hash and hashhash occurs right before
+ // __VA_ARGS__.
+ // cppreference.com: "some compilers offer an extension that allows ## to
+ // appear after a comma and before __VA_ARGS__, in which case the ## does
+ // nothing when the variable arguments are present, but removes the comma when
+ // the variable arguments are not present: this makes it possible to define
+ // macros such as fprintf (stderr, format, ##__VA_ARGS__)"
+ // FIXME: Handle named variadic macro parameters (also a GNU extension).
+
// If this is the first token to be printed, don't print space.
if (PrevTok.isNot(tok::unknown)) {
// If the tokens were already space separated, or if they must be to avoid
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 006a4006b7fc..1ccb0de92fba 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -582,9 +582,6 @@ bool ScanReachableSymbols::scan(SVal val) {
if (SymbolRef Sym = val.getAsSymbol())
return scan(Sym);
- if (const SymExpr *Sym = val.getAsSymbolicExpression())
- return scan(Sym);
-
if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
return scan(*X);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index cb6f61e86ae3..a481bde1651b 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -89,7 +89,7 @@ public:
}
TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
- BinaryOperatorKind QueriedOP) const {
+ BinaryOperatorKind QueriedOP) const {
return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
}
@@ -364,6 +364,18 @@ RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const {
return newRanges;
}
+RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F,
+ const llvm::APSInt &Point) const {
+ llvm::APSInt Upper = Point;
+ llvm::APSInt Lower = Point;
+
+ ++Upper;
+ --Lower;
+
+ // Notice that the lower bound is greater than the upper bound.
+ return Intersect(BV, F, Upper, Lower);
+}
+
void RangeSet::print(raw_ostream &os) const {
bool isFirst = true;
os << "{ ";
@@ -379,7 +391,315 @@ void RangeSet::print(raw_ostream &os) const {
os << " }";
}
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
+
namespace {
+class EquivalenceClass;
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
+REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
+
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
+
+namespace {
+/// This class encapsulates a set of symbols equal to each other.
+///
+/// The main idea of the approach requiring such classes is in narrowing
+/// and sharing constraints between symbols within the class. Also we can
+/// conclude that there is no practical need in storing constraints for
+/// every member of the class separately.
+///
+/// Main terminology:
+///
+/// * "Equivalence class" is an object of this class, which can be efficiently
+/// compared to other classes. It represents the whole class without
+/// storing the actual in it. The members of the class however can be
+/// retrieved from the state.
+///
+/// * "Class members" are the symbols corresponding to the class. This means
+/// that A == B for every member symbols A and B from the class. Members of
+/// each class are stored in the state.
+///
+/// * "Trivial class" is a class that has and ever had only one same symbol.
+///
+/// * "Merge operation" merges two classes into one. It is the main operation
+/// to produce non-trivial classes.
+/// If, at some point, we can assume that two symbols from two distinct
+/// classes are equal, we can merge these classes.
+class EquivalenceClass : public llvm::FoldingSetNode {
+public:
+ /// Find equivalence class for the given symbol in the given state.
+ LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State,
+ SymbolRef Sym);
+
+ /// Merge classes for the given symbols and return a new state.
+ LLVM_NODISCARD static inline ProgramStateRef
+ merge(BasicValueFactory &BV, RangeSet::Factory &F, ProgramStateRef State,
+ SymbolRef First, SymbolRef Second);
+ // Merge this class with the given class and return a new state.
+ LLVM_NODISCARD inline ProgramStateRef merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Other);
+
+ /// Return a set of class members for the given state.
+ LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State);
+ /// Return true if the current class is trivial in the given state.
+ LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State);
+ /// Return true if the current class is trivial and its only member is dead.
+ LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper);
+
+ LLVM_NODISCARD static inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef First, SymbolRef Second);
+ LLVM_NODISCARD static inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+ LLVM_NODISCARD inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass Other) const;
+ LLVM_NODISCARD static inline ClassSet
+ getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
+ LLVM_NODISCARD inline ClassSet
+ getDisequalClasses(ProgramStateRef State) const;
+ LLVM_NODISCARD inline ClassSet
+ getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
+
+ LLVM_NODISCARD static inline Optional<bool>
+ areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
+
+ /// Check equivalence data for consistency.
+ LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
+ isClassDataConsistent(ProgramStateRef State);
+
+ LLVM_NODISCARD QualType getType() const {
+ return getRepresentativeSymbol()->getType();
+ }
+
+ EquivalenceClass() = delete;
+ EquivalenceClass(const EquivalenceClass &) = default;
+ EquivalenceClass &operator=(const EquivalenceClass &) = delete;
+ EquivalenceClass(EquivalenceClass &&) = default;
+ EquivalenceClass &operator=(EquivalenceClass &&) = delete;
+
+ bool operator==(const EquivalenceClass &Other) const {
+ return ID == Other.ID;
+ }
+ bool operator<(const EquivalenceClass &Other) const { return ID < Other.ID; }
+ bool operator!=(const EquivalenceClass &Other) const {
+ return !operator==(Other);
+ }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, uintptr_t CID) {
+ ID.AddInteger(CID);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, this->ID); }
+
+private:
+ /* implicit */ EquivalenceClass(SymbolRef Sym)
+ : ID(reinterpret_cast<uintptr_t>(Sym)) {}
+
+ /// This function is intended to be used ONLY within the class.
+ /// The fact that ID is a pointer to a symbol is an implementation detail
+ /// and should stay that way.
+ /// In the current implementation, we use it to retrieve the only member
+ /// of the trivial class.
+ SymbolRef getRepresentativeSymbol() const {
+ return reinterpret_cast<SymbolRef>(ID);
+ }
+ static inline SymbolSet::Factory &getMembersFactory(ProgramStateRef State);
+
+ inline ProgramStateRef mergeImpl(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolSet Members,
+ EquivalenceClass Other,
+ SymbolSet OtherMembers);
+ static inline void
+ addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
+ BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+
+ /// This is a unique identifier of the class.
+ uintptr_t ID;
+};
+
+//===----------------------------------------------------------------------===//
+// Constraint functions
+//===----------------------------------------------------------------------===//
+
+LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
+ EquivalenceClass Class) {
+ return State->get<ConstraintRange>(Class);
+}
+
+LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
+ SymbolRef Sym) {
+ return getConstraint(State, EquivalenceClass::find(State, Sym));
+}
+
+//===----------------------------------------------------------------------===//
+// Equality/diseqiality abstraction
+//===----------------------------------------------------------------------===//
+
+/// A small helper structure representing symbolic equality.
+///
+/// Equality check can have different forms (like a == b or a - b) and this
+/// class encapsulates those away if the only thing the user wants to check -
+/// whether it's equality/diseqiality or not and have an easy access to the
+/// compared symbols.
+struct EqualityInfo {
+public:
+ SymbolRef Left, Right;
+ // true for equality and false for disequality.
+ bool IsEquality = true;
+
+ void invert() { IsEquality = !IsEquality; }
+ /// Extract equality information from the given symbol and the constants.
+ ///
+ /// This function assumes the following expression Sym + Adjustment != Int.
+ /// It is a default because the most widespread case of the equality check
+ /// is (A == B) + 0 != 0.
+ static Optional<EqualityInfo> extract(SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // As of now, the only equality form supported is Sym + 0 != 0.
+ if (!Int.isNullValue() || !Adjustment.isNullValue())
+ return llvm::None;
+
+ return extract(Sym);
+ }
+ /// Extract equality information from the given symbol.
+ static Optional<EqualityInfo> extract(SymbolRef Sym) {
+ return EqualityExtractor().Visit(Sym);
+ }
+
+private:
+ class EqualityExtractor
+ : public SymExprVisitor<EqualityExtractor, Optional<EqualityInfo>> {
+ public:
+ Optional<EqualityInfo> VisitSymSymExpr(const SymSymExpr *Sym) const {
+ switch (Sym->getOpcode()) {
+ case BO_Sub:
+ // This case is: A - B != 0 -> disequality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false};
+ case BO_EQ:
+ // This case is: A == B != 0 -> equality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), true};
+ case BO_NE:
+ // This case is: A != B != 0 -> diseqiality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false};
+ default:
+ return llvm::None;
+ }
+ }
+ };
+};
+
+//===----------------------------------------------------------------------===//
+// Intersection functions
+//===----------------------------------------------------------------------===//
+
+template <class SecondTy, class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
+
+template <class... RangeTy> struct IntersectionTraits;
+
+template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> {
+ // Found RangeSet, no need to check any further
+ using Type = RangeSet;
+};
+
+template <> struct IntersectionTraits<> {
+ // We ran out of types, and we didn't find any RangeSet, so the result should
+ // be optional.
+ using Type = Optional<RangeSet>;
+};
+
+template <class OptionalOrPointer, class... TailTy>
+struct IntersectionTraits<OptionalOrPointer, TailTy...> {
+ // If current type is Optional or a raw pointer, we should keep looking.
+ using Type = typename IntersectionTraits<TailTy...>::Type;
+};
+
+template <class EndTy>
+LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, EndTy End) {
+ // If the list contains only RangeSet or Optional<RangeSet>, simply return
+ // that range set.
+ return End;
+}
+
+LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet>
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) {
+ // This is an extraneous conversion from a raw pointer into Optional<RangeSet>
+ if (End) {
+ return *End;
+ }
+ return llvm::None;
+}
+
+template <class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
+ // Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version
+ // of the function and can be sure that the result is RangeSet.
+ return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...);
+}
+
+template <class SecondTy, class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
+ if (Second) {
+ // Here we call the <RangeSet,RangeSet,...> version of the function...
+ return intersect(BV, F, Head, *Second, Tail...);
+ }
+ // ...and here it is either <RangeSet,RangeSet,...> or <RangeSet,...>, which
+ // means that the result is definitely RangeSet.
+ return intersect(BV, F, Head, Tail...);
+}
+
+/// Main generic intersect function.
+/// It intersects all of the given range sets. If some of the given arguments
+/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+///
+/// Available representations for the arguments are:
+/// * RangeSet
+/// * Optional<RangeSet>
+/// * RangeSet *
+/// Pointer to a RangeSet is automatically assumed to be nullable and will get
+/// checked as well as the optional version. If this behaviour is undesired,
+/// please dereference the pointer in the call.
+///
+/// Return type depends on the arguments' types. If we can be sure in compile
+/// time that there will be a range set as a result, the returning type is
+/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>.
+///
+/// Please, prefer optional range sets to raw pointers. If the last argument is
+/// a raw pointer and all previous arguments are None, it will cost one
+/// additional check to convert RangeSet * into Optional<RangeSet>.
+template <class HeadTy, class SecondTy, class... RestTy>
+LLVM_NODISCARD inline
+ typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type
+ intersect(BasicValueFactory &BV, RangeSet::Factory &F, HeadTy Head,
+ SecondTy Second, RestTy... Tail) {
+ if (Head) {
+ return intersect(BV, F, *Head, Second, Tail...);
+ }
+ return intersect(BV, F, Second, Tail...);
+}
+
+//===----------------------------------------------------------------------===//
+// Symbolic reasoning logic
+//===----------------------------------------------------------------------===//
/// A little component aggregating all of the reasoning we have about
/// the ranges of symbolic expressions.
@@ -389,10 +709,11 @@ namespace {
class SymbolicRangeInferrer
: public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
public:
+ template <class SourceType>
static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
- ProgramStateRef State, SymbolRef Sym) {
+ ProgramStateRef State, SourceType Origin) {
SymbolicRangeInferrer Inferrer(BV, F, State);
- return Inferrer.infer(Sym);
+ return Inferrer.infer(Origin);
}
RangeSet VisitSymExpr(SymbolRef Sym) {
@@ -442,37 +763,35 @@ private:
}
RangeSet infer(SymbolRef Sym) {
- const RangeSet *AssociatedRange = State->get<ConstraintRange>(Sym);
-
- // If Sym is a difference of symbols A - B, then maybe we have range set
- // stored for B - A.
- const RangeSet *RangeAssociatedWithNegatedSym =
- getRangeForMinusSymbol(State, Sym);
-
- // If we have range set stored for both A - B and B - A then calculate the
- // effective range set by intersecting the range set for A - B and the
- // negated range set of B - A.
- if (AssociatedRange && RangeAssociatedWithNegatedSym)
- return AssociatedRange->Intersect(
- ValueFactory, RangeFactory,
- RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory));
-
- if (AssociatedRange)
- return *AssociatedRange;
-
- if (RangeAssociatedWithNegatedSym)
- return RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory);
+ if (Optional<RangeSet> ConstraintBasedRange = intersect(
+ ValueFactory, RangeFactory, getConstraint(State, Sym),
+ // If Sym is a difference of symbols A - B, then maybe we have range
+ // set stored for B - A.
+ //
+ // If we have range set stored for both A - B and B - A then
+ // calculate the effective range set by intersecting the range set
+ // for A - B and the negated range set of B - A.
+ getRangeForNegatedSub(Sym), getRangeForEqualities(Sym))) {
+ return *ConstraintBasedRange;
+ }
// If Sym is a comparison expression (except <=>),
// find any other comparisons with the same operands.
// See function description.
- const RangeSet CmpRangeSet = getRangeForComparisonSymbol(State, Sym);
- if (!CmpRangeSet.isEmpty())
- return CmpRangeSet;
+ if (Optional<RangeSet> CmpRangeSet = getRangeForComparisonSymbol(Sym)) {
+ return *CmpRangeSet;
+ }
return Visit(Sym);
}
+ RangeSet infer(EquivalenceClass Class) {
+ if (const RangeSet *AssociatedConstraint = getConstraint(State, Class))
+ return *AssociatedConstraint;
+
+ return infer(Class.getType());
+ }
+
/// Infer range information solely from the type.
RangeSet infer(QualType T) {
// Lazily generate a new RangeSet representing all possible values for the
@@ -621,8 +940,7 @@ private:
/// Return a range set subtracting zero from \p Domain.
RangeSet assumeNonZero(RangeSet Domain, QualType T) {
APSIntType IntType = ValueFactory.getAPSIntType(T);
- return Domain.Intersect(ValueFactory, RangeFactory,
- ++IntType.getZeroValue(), --IntType.getZeroValue());
+ return Domain.Delete(ValueFactory, RangeFactory, IntType.getZeroValue());
}
// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
@@ -630,23 +948,26 @@ private:
// symbol manually. This will allow us to support finding ranges of not
// only negated SymSymExpr-type expressions, but also of other, simpler
// expressions which we currently do not know how to negate.
- const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym) {
+ Optional<RangeSet> getRangeForNegatedSub(SymbolRef Sym) {
if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
if (SSE->getOpcode() == BO_Sub) {
QualType T = Sym->getType();
+
+ // Do not negate unsigned ranges
+ if (!T->isUnsignedIntegerOrEnumerationType() &&
+ !T->isSignedIntegerOrEnumerationType())
+ return llvm::None;
+
SymbolManager &SymMgr = State->getSymbolManager();
- SymbolRef negSym =
+ SymbolRef NegatedSym =
SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T);
- if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
- // Unsigned range set cannot be negated, unless it is [0, 0].
- if (T->isUnsignedIntegerOrEnumerationType() ||
- T->isSignedIntegerOrEnumerationType())
- return negV;
+ if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) {
+ return NegatedRange->Negate(ValueFactory, RangeFactory);
}
}
}
- return nullptr;
+ return llvm::None;
}
// Returns ranges only for binary comparison operators (except <=>)
@@ -659,18 +980,16 @@ private:
// It covers all possible combinations (see CmpOpTable description).
// Note that `x` and `y` can also stand for subexpressions,
// not only for actual symbols.
- RangeSet getRangeForComparisonSymbol(ProgramStateRef State, SymbolRef Sym) {
- const RangeSet EmptyRangeSet = RangeFactory.getEmptySet();
-
- auto SSE = dyn_cast<SymSymExpr>(Sym);
+ Optional<RangeSet> getRangeForComparisonSymbol(SymbolRef Sym) {
+ const auto *SSE = dyn_cast<SymSymExpr>(Sym);
if (!SSE)
- return EmptyRangeSet;
+ return llvm::None;
BinaryOperatorKind CurrentOP = SSE->getOpcode();
// We currently do not support <=> (C++20).
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
- return EmptyRangeSet;
+ return llvm::None;
static const OperatorRelationsTable CmpOpTable{};
@@ -679,10 +998,6 @@ private:
QualType T = SSE->getType();
SymbolManager &SymMgr = State->getSymbolManager();
- const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
- const llvm::APSInt &One = ValueFactory.getValue(1, T);
- const RangeSet TrueRangeSet(RangeFactory, One, One);
- const RangeSet FalseRangeSet(RangeFactory, Zero, Zero);
int UnknownStates = 0;
@@ -693,7 +1008,7 @@ private:
// Let's find an expression e.g. (x < y).
BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i);
const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
- const RangeSet *QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ const RangeSet *QueriedRangeSet = getConstraint(State, SymSym);
// If ranges were not previously found,
// try to find a reversed expression (y > x).
@@ -701,7 +1016,7 @@ private:
const BinaryOperatorKind ROP =
BinaryOperator::reverseComparisonOp(QueriedOP);
SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
- QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ QueriedRangeSet = getConstraint(State, SymSym);
}
if (!QueriedRangeSet || QueriedRangeSet->isEmpty())
@@ -732,11 +1047,38 @@ private:
continue;
}
- return (BranchState == OperatorRelationsTable::True) ? TrueRangeSet
- : FalseRangeSet;
+ return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)
+ : getFalseRange(T);
+ }
+
+ return llvm::None;
+ }
+
+ Optional<RangeSet> getRangeForEqualities(SymbolRef Sym) {
+ Optional<EqualityInfo> Equality = EqualityInfo::extract(Sym);
+
+ if (!Equality)
+ return llvm::None;
+
+ if (Optional<bool> AreEqual = EquivalenceClass::areEqual(
+ State, Equality->Left, Equality->Right)) {
+ if (*AreEqual == Equality->IsEquality) {
+ return getTrueRange(Sym->getType());
+ }
+ return getFalseRange(Sym->getType());
}
- return EmptyRangeSet;
+ return llvm::None;
+ }
+
+ RangeSet getTrueRange(QualType T) {
+ RangeSet TypeRange = infer(T);
+ return assumeNonZero(TypeRange, T);
+ }
+
+ RangeSet getFalseRange(QualType T) {
+ const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
+ return RangeSet(RangeFactory, Zero);
}
BasicValueFactory &ValueFactory;
@@ -744,6 +1086,10 @@ private:
ProgramStateRef State;
};
+//===----------------------------------------------------------------------===//
+// Range-based reasoning about symbolic operations
+//===----------------------------------------------------------------------===//
+
template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
QualType T) {
@@ -904,6 +1250,10 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
}
+//===----------------------------------------------------------------------===//
+// Constraint manager implementation details
+//===----------------------------------------------------------------------===//
+
class RangeConstraintManager : public RangedConstraintManager {
public:
RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
@@ -915,7 +1265,11 @@ public:
bool haveEqualConstraints(ProgramStateRef S1,
ProgramStateRef S2) const override {
- return S1->get<ConstraintRange>() == S2->get<ConstraintRange>();
+ // NOTE: ClassMembers are as simple as back pointers for ClassMap,
+ // so comparing constraint ranges and class maps should be
+ // sufficient.
+ return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
+ S1->get<ClassMap>() == S2->get<ClassMap>();
}
bool canReasonAbout(SVal X) const override;
@@ -971,6 +1325,7 @@ private:
RangeSet::Factory F;
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+ RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
@@ -987,6 +1342,87 @@ private:
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
+
+ //===------------------------------------------------------------------===//
+ // Equality tracking implementation
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef trackEQ(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ return track<true>(NewConstraint, State, Sym, Int, Adjustment);
+ }
+
+ ProgramStateRef trackNE(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ return track<false>(NewConstraint, State, Sym, Int, Adjustment);
+ }
+
+ template <bool EQ>
+ ProgramStateRef track(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ if (NewConstraint.isEmpty())
+ // This is an infeasible assumption.
+ return nullptr;
+
+ ProgramStateRef NewState = setConstraint(State, Sym, NewConstraint);
+ if (auto Equality = EqualityInfo::extract(Sym, Int, Adjustment)) {
+ // If the original assumption is not Sym + Adjustment !=/</> Int,
+ // we should invert IsEquality flag.
+ Equality->IsEquality = Equality->IsEquality != EQ;
+ return track(NewState, *Equality);
+ }
+
+ return NewState;
+ }
+
+ ProgramStateRef track(ProgramStateRef State, EqualityInfo ToTrack) {
+ if (ToTrack.IsEquality) {
+ return trackEquality(State, ToTrack.Left, ToTrack.Right);
+ }
+ return trackDisequality(State, ToTrack.Left, ToTrack.Right);
+ }
+
+ ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
+ SymbolRef RHS) {
+ return EquivalenceClass::markDisequal(getBasicVals(), F, State, LHS, RHS);
+ }
+
+ ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS,
+ SymbolRef RHS) {
+ return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS);
+ }
+
+ LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State,
+ EquivalenceClass Class,
+ RangeSet Constraint) {
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CF = State->get_context<ConstraintRange>();
+
+ // Add new constraint.
+ Constraints = CF.add(Constraints, Class, Constraint);
+
+ // There is a chance that we might need to update constraints for the
+ // classes that are known to be disequal to Class.
+ //
+ // In order for this to be even possible, the new constraint should
+ // be simply a constant because we can't reason about range disequalities.
+ if (const llvm::APSInt *Point = Constraint.getConcreteValue())
+ for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) {
+ RangeSet UpdatedConstraint =
+ getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point);
+ Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint);
+ }
+
+ return State->set<ConstraintRange>(Constraints);
+ }
+
+ LLVM_NODISCARD inline ProgramStateRef
+ setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) {
+ return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint);
+ }
};
} // end anonymous namespace
@@ -997,6 +1433,372 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr,
return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
+ConstraintMap ento::getConstraintMap(ProgramStateRef State) {
+ ConstraintMap::Factory &F = State->get_context<ConstraintMap>();
+ ConstraintMap Result = F.getEmptyMap();
+
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ for (std::pair<EquivalenceClass, RangeSet> ClassConstraint : Constraints) {
+ EquivalenceClass Class = ClassConstraint.first;
+ SymbolSet ClassMembers = Class.getClassMembers(State);
+ assert(!ClassMembers.isEmpty() &&
+ "Class must always have at least one member!");
+
+ SymbolRef Representative = *ClassMembers.begin();
+ Result = F.add(Result, Representative, ClassConstraint.second);
+ }
+
+ return Result;
+}
+
+//===----------------------------------------------------------------------===//
+// EqualityClass implementation details
+//===----------------------------------------------------------------------===//
+
+inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State,
+ SymbolRef Sym) {
+ // We store far from all Symbol -> Class mappings
+ if (const EquivalenceClass *NontrivialClass = State->get<ClassMap>(Sym))
+ return *NontrivialClass;
+
+ // This is a trivial class of Sym.
+ return Sym;
+}
+
+inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second) {
+ EquivalenceClass FirstClass = find(State, First);
+ EquivalenceClass SecondClass = find(State, Second);
+
+ return FirstClass.merge(BV, F, State, SecondClass);
+}
+
+inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Other) {
+ // It is already the same class.
+ if (*this == Other)
+ return State;
+
+ // FIXME: As of now, we support only equivalence classes of the same type.
+ // This limitation is connected to the lack of explicit casts in
+ // our symbolic expression model.
+ //
+ // That means that for `int x` and `char y` we don't distinguish
+ // between these two very different cases:
+ // * `x == y`
+ // * `(char)x == y`
+ //
+ // The moment we introduce symbolic casts, this restriction can be
+ // lifted.
+ if (getType() != Other.getType())
+ return State;
+
+ SymbolSet Members = getClassMembers(State);
+ SymbolSet OtherMembers = Other.getClassMembers(State);
+
+ // We estimate the size of the class by the height of tree containing
+ // its members. Merging is not a trivial operation, so it's easier to
+ // merge the smaller class into the bigger one.
+ if (Members.getHeight() >= OtherMembers.getHeight()) {
+ return mergeImpl(BV, F, State, Members, Other, OtherMembers);
+ } else {
+ return Other.mergeImpl(BV, F, State, OtherMembers, *this, Members);
+ }
+}
+
+inline ProgramStateRef
+EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
+ RangeSet::Factory &RangeFactory,
+ ProgramStateRef State, SymbolSet MyMembers,
+ EquivalenceClass Other, SymbolSet OtherMembers) {
+ // Essentially what we try to recreate here is some kind of union-find
+ // data structure. It does have certain limitations due to persistence
+ // and the need to remove elements from classes.
+ //
+ // In this setting, EquialityClass object is the representative of the class
+ // or the parent element. ClassMap is a mapping of class members to their
+ // parent. Unlike the union-find structure, they all point directly to the
+ // class representative because we don't have an opportunity to actually do
+ // path compression when dealing with immutability. This means that we
+ // compress paths every time we do merges. It also means that we lose
+ // the main amortized complexity benefit from the original data structure.
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
+
+ // 1. If the merged classes have any constraints associated with them, we
+ // need to transfer them to the class we have left.
+ //
+ // Intersection here makes perfect sense because both of these constraints
+ // must hold for the whole new class.
+ if (Optional<RangeSet> NewClassConstraint =
+ intersect(ValueFactory, RangeFactory, getConstraint(State, *this),
+ getConstraint(State, Other))) {
+ // NOTE: Essentially, NewClassConstraint should NEVER be infeasible because
+ // range inferrer shouldn't generate ranges incompatible with
+ // equivalence classes. However, at the moment, due to imperfections
+ // in the solver, it is possible and the merge function can also
+ // return infeasible states aka null states.
+ if (NewClassConstraint->isEmpty())
+ // Infeasible state
+ return nullptr;
+
+ // No need in tracking constraints of a now-dissolved class.
+ Constraints = CRF.remove(Constraints, Other);
+ // Assign new constraints for this class.
+ Constraints = CRF.add(Constraints, *this, *NewClassConstraint);
+
+ State = State->set<ConstraintRange>(Constraints);
+ }
+
+ // 2. Get ALL equivalence-related maps
+ ClassMapTy Classes = State->get<ClassMap>();
+ ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
+
+ ClassMembersTy Members = State->get<ClassMembers>();
+ ClassMembersTy::Factory &MF = State->get_context<ClassMembers>();
+
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ DisequalityMapTy::Factory &DF = State->get_context<DisequalityMap>();
+
+ ClassSet::Factory &CF = State->get_context<ClassSet>();
+ SymbolSet::Factory &F = getMembersFactory(State);
+
+ // 2. Merge members of the Other class into the current class.
+ SymbolSet NewClassMembers = MyMembers;
+ for (SymbolRef Sym : OtherMembers) {
+ NewClassMembers = F.add(NewClassMembers, Sym);
+ // *this is now the class for all these new symbols.
+ Classes = CMF.add(Classes, Sym, *this);
+ }
+
+ // 3. Adjust member mapping.
+ //
+ // No need in tracking members of a now-dissolved class.
+ Members = MF.remove(Members, Other);
+ // Now only the current class is mapped to all the symbols.
+ Members = MF.add(Members, *this, NewClassMembers);
+
+ // 4. Update disequality relations
+ ClassSet DisequalToOther = Other.getDisequalClasses(DisequalityInfo, CF);
+ if (!DisequalToOther.isEmpty()) {
+ ClassSet DisequalToThis = getDisequalClasses(DisequalityInfo, CF);
+ DisequalityInfo = DF.remove(DisequalityInfo, Other);
+
+ for (EquivalenceClass DisequalClass : DisequalToOther) {
+ DisequalToThis = CF.add(DisequalToThis, DisequalClass);
+
+ // Disequality is a symmetric relation meaning that if
+ // DisequalToOther not null then the set for DisequalClass is not
+ // empty and has at least Other.
+ ClassSet OriginalSetLinkedToOther =
+ *DisequalityInfo.lookup(DisequalClass);
+
+ // Other will be eliminated and we should replace it with the bigger
+ // united class.
+ ClassSet NewSet = CF.remove(OriginalSetLinkedToOther, Other);
+ NewSet = CF.add(NewSet, *this);
+
+ DisequalityInfo = DF.add(DisequalityInfo, DisequalClass, NewSet);
+ }
+
+ DisequalityInfo = DF.add(DisequalityInfo, *this, DisequalToThis);
+ State = State->set<DisequalityMap>(DisequalityInfo);
+ }
+
+ // 5. Update the state
+ State = State->set<ClassMap>(Classes);
+ State = State->set<ClassMembers>(Members);
+
+ return State;
+}
+
+inline SymbolSet::Factory &
+EquivalenceClass::getMembersFactory(ProgramStateRef State) {
+ return State->get_context<SymbolSet>();
+}
+
+SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) {
+ if (const SymbolSet *Members = State->get<ClassMembers>(*this))
+ return *Members;
+
+ // This class is trivial, so we need to construct a set
+ // with just that one symbol from the class.
+ SymbolSet::Factory &F = getMembersFactory(State);
+ return F.add(F.getEmptySet(), getRepresentativeSymbol());
+}
+
+bool EquivalenceClass::isTrivial(ProgramStateRef State) {
+ return State->get<ClassMembers>(*this) == nullptr;
+}
+
+bool EquivalenceClass::isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper) {
+ return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
+}
+
+inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
+ RangeSet::Factory &RF,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second) {
+ return markDisequal(VF, RF, State, find(State, First), find(State, Second));
+}
+
+inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
+ RangeSet::Factory &RF,
+ ProgramStateRef State,
+ EquivalenceClass First,
+ EquivalenceClass Second) {
+ return First.markDisequal(VF, RF, State, Second);
+}
+
+inline ProgramStateRef
+EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF,
+ ProgramStateRef State,
+ EquivalenceClass Other) const {
+ // If we know that two classes are equal, we can only produce an infeasible
+ // state.
+ if (*this == Other) {
+ return nullptr;
+ }
+
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+
+ // Disequality is a symmetric relation, so if we mark A as disequal to B,
+ // we should also mark B as disequalt to A.
+ addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this,
+ Other);
+ addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other,
+ *this);
+
+ State = State->set<DisequalityMap>(DisequalityInfo);
+ State = State->set<ConstraintRange>(Constraints);
+
+ return State;
+}
+
+inline void EquivalenceClass::addToDisequalityInfo(
+ DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
+ BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State,
+ EquivalenceClass First, EquivalenceClass Second) {
+
+ // 1. Get all of the required factories.
+ DisequalityMapTy::Factory &F = State->get_context<DisequalityMap>();
+ ClassSet::Factory &CF = State->get_context<ClassSet>();
+ ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
+
+ // 2. Add Second to the set of classes disequal to First.
+ const ClassSet *CurrentSet = Info.lookup(First);
+ ClassSet NewSet = CurrentSet ? *CurrentSet : CF.getEmptySet();
+ NewSet = CF.add(NewSet, Second);
+
+ Info = F.add(Info, First, NewSet);
+
+ // 3. If Second is known to be a constant, we can delete this point
+ // from the constraint asociated with First.
+ //
+ // So, if Second == 10, it means that First != 10.
+ // At the same time, the same logic does not apply to ranges.
+ if (const RangeSet *SecondConstraint = Constraints.lookup(Second))
+ if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) {
+
+ RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange(
+ VF, RF, State, First.getRepresentativeSymbol());
+
+ FirstConstraint = FirstConstraint.Delete(VF, RF, *Point);
+ Constraints = CRF.add(Constraints, First, FirstConstraint);
+ }
+}
+
+inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ SymbolRef FirstSym,
+ SymbolRef SecondSym) {
+ EquivalenceClass First = find(State, FirstSym);
+ EquivalenceClass Second = find(State, SecondSym);
+
+ // The same equivalence class => symbols are equal.
+ if (First == Second)
+ return true;
+
+ // Let's check if we know anything about these two classes being not equal to
+ // each other.
+ ClassSet DisequalToFirst = First.getDisequalClasses(State);
+ if (DisequalToFirst.contains(Second))
+ return false;
+
+ // It is not clear.
+ return llvm::None;
+}
+
+inline ClassSet EquivalenceClass::getDisequalClasses(ProgramStateRef State,
+ SymbolRef Sym) {
+ return find(State, Sym).getDisequalClasses(State);
+}
+
+inline ClassSet
+EquivalenceClass::getDisequalClasses(ProgramStateRef State) const {
+ return getDisequalClasses(State->get<DisequalityMap>(),
+ State->get_context<ClassSet>());
+}
+
+inline ClassSet
+EquivalenceClass::getDisequalClasses(DisequalityMapTy Map,
+ ClassSet::Factory &Factory) const {
+ if (const ClassSet *DisequalClasses = Map.lookup(*this))
+ return *DisequalClasses;
+
+ return Factory.getEmptySet();
+}
+
+bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
+ ClassMembersTy Members = State->get<ClassMembers>();
+
+ for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair : Members) {
+ for (SymbolRef Member : ClassMembersPair.second) {
+ // Every member of the class should have a mapping back to the class.
+ if (find(State, Member) == ClassMembersPair.first) {
+ continue;
+ }
+
+ return false;
+ }
+ }
+
+ DisequalityMapTy Disequalities = State->get<DisequalityMap>();
+ for (std::pair<EquivalenceClass, ClassSet> DisequalityInfo : Disequalities) {
+ EquivalenceClass Class = DisequalityInfo.first;
+ ClassSet DisequalClasses = DisequalityInfo.second;
+
+ // There is no use in keeping empty sets in the map.
+ if (DisequalClasses.isEmpty())
+ return false;
+
+ // Disequality is symmetrical, i.e. for every Class A and B that A != B,
+ // B != A should also be true.
+ for (EquivalenceClass DisequalClass : DisequalClasses) {
+ const ClassSet *DisequalToDisequalClasses =
+ Disequalities.lookup(DisequalClass);
+
+ // It should be a set of at least one element: Class
+ if (!DisequalToDisequalClasses ||
+ !DisequalToDisequalClasses->contains(Class))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// RangeConstraintManager implementation
+//===----------------------------------------------------------------------===//
+
bool RangeConstraintManager::canReasonAbout(SVal X) const {
Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
@@ -1045,7 +1847,7 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const {
ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
SymbolRef Sym) {
- const RangeSet *Ranges = State->get<ConstraintRange>(Sym);
+ const RangeSet *Ranges = getConstraint(State, Sym);
// If we don't have any information about this symbol, it's underconstrained.
if (!Ranges)
@@ -1069,28 +1871,148 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
SymbolRef Sym) const {
- const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym);
+ const RangeSet *T = getConstraint(St, Sym);
return T ? T->getConcreteValue() : nullptr;
}
+//===----------------------------------------------------------------------===//
+// Remove dead symbols from existing constraints
+//===----------------------------------------------------------------------===//
+
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
ProgramStateRef
RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
SymbolReaper &SymReaper) {
- bool Changed = false;
- ConstraintRangeTy CR = State->get<ConstraintRange>();
- ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>();
+ ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
+ ClassMembersTy NewClassMembersMap = ClassMembersMap;
+ ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
+ SymbolSet::Factory &SetFactory = State->get_context<SymbolSet>();
+
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy NewConstraints = Constraints;
+ ConstraintRangeTy::Factory &ConstraintFactory =
+ State->get_context<ConstraintRange>();
+
+ ClassMapTy Map = State->get<ClassMap>();
+ ClassMapTy NewMap = Map;
+ ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>();
+
+ DisequalityMapTy Disequalities = State->get<DisequalityMap>();
+ DisequalityMapTy::Factory &DisequalityFactory =
+ State->get_context<DisequalityMap>();
+ ClassSet::Factory &ClassSetFactory = State->get_context<ClassSet>();
+
+ bool ClassMapChanged = false;
+ bool MembersMapChanged = false;
+ bool ConstraintMapChanged = false;
+ bool DisequalitiesChanged = false;
+
+ auto removeDeadClass = [&](EquivalenceClass Class) {
+ // Remove associated constraint ranges.
+ Constraints = ConstraintFactory.remove(Constraints, Class);
+ ConstraintMapChanged = true;
+
+ // Update disequality information to not hold any information on the
+ // removed class.
+ ClassSet DisequalClasses =
+ Class.getDisequalClasses(Disequalities, ClassSetFactory);
+ if (!DisequalClasses.isEmpty()) {
+ for (EquivalenceClass DisequalClass : DisequalClasses) {
+ ClassSet DisequalToDisequalSet =
+ DisequalClass.getDisequalClasses(Disequalities, ClassSetFactory);
+ // DisequalToDisequalSet is guaranteed to be non-empty for consistent
+ // disequality info.
+ assert(!DisequalToDisequalSet.isEmpty());
+ ClassSet NewSet = ClassSetFactory.remove(DisequalToDisequalSet, Class);
+
+ // No need in keeping an empty set.
+ if (NewSet.isEmpty()) {
+ Disequalities =
+ DisequalityFactory.remove(Disequalities, DisequalClass);
+ } else {
+ Disequalities =
+ DisequalityFactory.add(Disequalities, DisequalClass, NewSet);
+ }
+ }
+ // Remove the data for the class
+ Disequalities = DisequalityFactory.remove(Disequalities, Class);
+ DisequalitiesChanged = true;
+ }
+ };
+
+ // 1. Let's see if dead symbols are trivial and have associated constraints.
+ for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair :
+ Constraints) {
+ EquivalenceClass Class = ClassConstraintPair.first;
+ if (Class.isTriviallyDead(State, SymReaper)) {
+ // If this class is trivial, we can remove its constraints right away.
+ removeDeadClass(Class);
+ }
+ }
+
+ // 2. We don't need to track classes for dead symbols.
+ for (std::pair<SymbolRef, EquivalenceClass> SymbolClassPair : Map) {
+ SymbolRef Sym = SymbolClassPair.first;
- for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
- SymbolRef Sym = I.getKey();
if (SymReaper.isDead(Sym)) {
- Changed = true;
- CR = CRFactory.remove(CR, Sym);
+ ClassMapChanged = true;
+ NewMap = ClassFactory.remove(NewMap, Sym);
}
}
- return Changed ? State->set<ConstraintRange>(CR) : State;
+ // 3. Remove dead members from classes and remove dead non-trivial classes
+ // and their constraints.
+ for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair :
+ ClassMembersMap) {
+ EquivalenceClass Class = ClassMembersPair.first;
+ SymbolSet LiveMembers = ClassMembersPair.second;
+ bool MembersChanged = false;
+
+ for (SymbolRef Member : ClassMembersPair.second) {
+ if (SymReaper.isDead(Member)) {
+ MembersChanged = true;
+ LiveMembers = SetFactory.remove(LiveMembers, Member);
+ }
+ }
+
+ // Check if the class changed.
+ if (!MembersChanged)
+ continue;
+
+ MembersMapChanged = true;
+
+ if (LiveMembers.isEmpty()) {
+ // The class is dead now, we need to wipe it out of the members map...
+ NewClassMembersMap = EMFactory.remove(NewClassMembersMap, Class);
+
+ // ...and remove all of its constraints.
+ removeDeadClass(Class);
+ } else {
+ // We need to change the members associated with the class.
+ NewClassMembersMap =
+ EMFactory.add(NewClassMembersMap, Class, LiveMembers);
+ }
+ }
+
+ // 4. Update the state with new maps.
+ //
+ // Here we try to be humble and update a map only if it really changed.
+ if (ClassMapChanged)
+ State = State->set<ClassMap>(NewMap);
+
+ if (MembersMapChanged)
+ State = State->set<ClassMembers>(NewClassMembersMap);
+
+ if (ConstraintMapChanged)
+ State = State->set<ConstraintRange>(Constraints);
+
+ if (DisequalitiesChanged)
+ State = State->set<DisequalityMap>(Disequalities);
+
+ assert(EquivalenceClass::isClassDataConsistent(State));
+
+ return State;
}
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
@@ -1098,6 +2020,11 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
}
+RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
+ EquivalenceClass Class) {
+ return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Class);
+}
+
//===------------------------------------------------------------------------===
// assumeSymX methods: protected interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
@@ -1119,15 +2046,11 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
return St;
- llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment;
- llvm::APSInt Upper = Lower;
- --Lower;
- ++Upper;
+ llvm::APSInt Point = AdjustmentType.convert(Int) - Adjustment;
- // [Int-Adjustment+1, Int-Adjustment-1]
- // Notice that the lower bound is greater than the upper bound.
- RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ RangeSet New = getRange(St, Sym).Delete(getBasicVals(), F, Point);
+
+ return trackNE(New, St, Sym, Int, Adjustment);
}
ProgramStateRef
@@ -1142,7 +2065,8 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
// [Int-Adjustment, Int-Adjustment]
llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+
+ return trackEQ(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St,
@@ -1178,7 +2102,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLTRange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return trackNE(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St,
@@ -1214,7 +2138,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGTRange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return trackNE(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St,
@@ -1250,13 +2174,13 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGERange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(St, Sym, New);
}
-RangeSet RangeConstraintManager::getSymLERange(
- llvm::function_ref<RangeSet()> RS,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment) {
+RangeSet
+RangeConstraintManager::getSymLERange(llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
// Before we do any real work, see if the value can even show up.
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
@@ -1293,7 +2217,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLERange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(St, Sym, New);
}
ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
@@ -1303,7 +2227,7 @@ ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
if (New.isEmpty())
return nullptr;
RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment);
- return Out.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, Out);
+ return Out.isEmpty() ? nullptr : setConstraint(State, Sym, Out);
}
ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
@@ -1312,7 +2236,7 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment);
RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment);
RangeSet New(RangeLT.addRange(F, RangeGT));
- return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(State, Sym, New);
}
//===----------------------------------------------------------------------===//
@@ -1332,17 +2256,25 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
++Space;
Out << '[' << NL;
- for (ConstraintRangeTy::iterator I = Constraints.begin();
- I != Constraints.end(); ++I) {
- Indent(Out, Space, IsDot)
- << "{ \"symbol\": \"" << I.getKey() << "\", \"range\": \"";
- I.getData().print(Out);
- Out << "\" }";
-
- if (std::next(I) != Constraints.end())
- Out << ',';
- Out << NL;
+ bool First = true;
+ for (std::pair<EquivalenceClass, RangeSet> P : Constraints) {
+ SymbolSet ClassMembers = P.first.getClassMembers(State);
+
+ // We can print the same constraint for every class member.
+ for (SymbolRef ClassMember : ClassMembers) {
+ if (First) {
+ First = false;
+ } else {
+ Out << ',';
+ Out << NL;
+ }
+ Indent(Out, Space, IsDot)
+ << "{ \"symbol\": \"" << ClassMember << "\", \"range\": \"";
+ P.second.print(Out);
+ Out << "\" }";
+ }
}
+ Out << NL;
--Space;
Indent(Out, Space, IsDot) << "]," << NL;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
index 4748c106eb55..e7a03e6ed582 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -40,19 +40,20 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
}
} else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- // Translate "a != b" to "(b - a) != 0".
- // We invert the order of the operands as a heuristic for how loop
- // conditions are usually written ("begin != end") as compared to length
- // calculations ("end - begin"). The more correct thing to do would be to
- // canonicalize "a - b" and "b - a", which would allow us to treat
- // "a != b" and "b != a" the same.
- SymbolManager &SymMgr = getSymbolManager();
BinaryOperator::Opcode Op = SSE->getOpcode();
assert(BinaryOperator::isComparisonOp(Op));
- // For now, we only support comparing pointers.
+ // We convert equality operations for pointers only.
if (Loc::isLocType(SSE->getLHS()->getType()) &&
Loc::isLocType(SSE->getRHS()->getType())) {
+ // Translate "a != b" to "(b - a) != 0".
+ // We invert the order of the operands as a heuristic for how loop
+ // conditions are usually written ("begin != end") as compared to length
+ // calculations ("end - begin"). The more correct thing to do would be to
+ // canonicalize "a - b" and "b - a", which would allow us to treat
+ // "a != b" and "b != a" the same.
+
+ SymbolManager &SymMgr = getSymbolManager();
QualType DiffTy = SymMgr.getContext().getPointerDiffType();
SymbolRef Subtraction =
SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
@@ -63,6 +64,25 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
Op = BinaryOperator::negateComparisonOp(Op);
return assumeSymRel(State, Subtraction, Op, Zero);
}
+
+ if (BinaryOperator::isEqualityOp(Op)) {
+ SymbolManager &SymMgr = getSymbolManager();
+
+ QualType ExprType = SSE->getType();
+ SymbolRef CanonicalEquality =
+ SymMgr.getSymSymExpr(SSE->getLHS(), BO_EQ, SSE->getRHS(), ExprType);
+
+ bool WasEqual = SSE->getOpcode() == BO_EQ;
+ bool IsExpectedEqual = WasEqual == Assumption;
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, ExprType);
+
+ if (IsExpectedEqual) {
+ return assumeSymNE(State, CanonicalEquality, Zero, Zero);
+ }
+
+ return assumeSymEQ(State, CanonicalEquality, Zero, Zero);
+ }
}
// If we get here, there's nothing else we can do but treat the symbol as
@@ -199,11 +219,6 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
}
}
-void *ProgramStateTrait<ConstraintRange>::GDMIndex() {
- static int Index;
- return &Index;
-}
-
} // end of namespace ento
} // end of namespace clang
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index c00a2c8ba8a2..72b8ada1dfab 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -236,10 +236,11 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
return nonloc::SymbolVal(sym);
}
-DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
- assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD));
+DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
+ assert(!ND || isa<CXXMethodDecl>(ND) || isa<FieldDecl>(ND) ||
+ isa<IndirectFieldDecl>(ND));
- if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) {
// Sema treats pointers to static member functions as have function pointer
// type, so return a function pointer for the method.
// We don't need to play a similar trick for static member fields
@@ -249,7 +250,7 @@ DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
return getFunctionPointer(MD);
}
- return nonloc::PointerToMember(DD);
+ return nonloc::PointerToMember(ND);
}
DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) {
@@ -305,6 +306,14 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
return makeLoc(getRegionManager().getStringRegion(SL));
}
+ case Stmt::PredefinedExprClass: {
+ const auto *PE = cast<PredefinedExpr>(E);
+ assert(PE->getFunctionName() &&
+ "Since we analyze only instantiated functions, PredefinedExpr "
+ "should have a function name.");
+ return makeLoc(getRegionManager().getStringRegion(PE->getFunctionName()));
+ }
+
// Fast-path some expressions to avoid the overhead of going through the AST's
// constant evaluator
case Stmt::CharacterLiteralClass: {
@@ -377,8 +386,8 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
NonLoc LHS, NonLoc RHS,
QualType ResultTy) {
- const SymExpr *symLHS = LHS.getAsSymExpr();
- const SymExpr *symRHS = RHS.getAsSymExpr();
+ SymbolRef symLHS = LHS.getAsSymbol();
+ SymbolRef symRHS = RHS.getAsSymbol();
// TODO: When the Max Complexity is reached, we should conjure a symbol
// instead of generating an Unknown value and propagate the taint info to it.
@@ -492,7 +501,7 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy))
return evalCast(val, castTy, originalTy);
- const SymExpr *se = val.getAsSymbolicExpression();
+ SymbolRef se = val.getAsSymbol();
if (!se) // Let evalCast handle non symbolic expressions.
return evalCast(val, castTy, originalTy);
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
index 9b5de6c3eb92..252596887e4f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -84,16 +84,12 @@ const FunctionDecl *SVal::getAsFunctionDecl() const {
/// the first symbolic parent region is returned.
SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const {
// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
- if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
- return X->getLoc().getAsLocSymbol(IncludeBaseRegions);
-
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
- const MemRegion *R = X->getRegion();
- if (const SymbolicRegion *SymR = IncludeBaseRegions ?
- R->getSymbolicBase() :
- dyn_cast<SymbolicRegion>(R->StripCasts()))
+ if (const MemRegion *R = getAsRegion())
+ if (const SymbolicRegion *SymR =
+ IncludeBaseRegions ? R->getSymbolicBase()
+ : dyn_cast<SymbolicRegion>(R->StripCasts()))
return SymR->getSymbol();
- }
+
return nullptr;
}
@@ -116,8 +112,6 @@ SymbolRef SVal::getLocSymbolInBase() const {
return nullptr;
}
-// TODO: The next 3 functions have to be simplified.
-
/// If this SVal wraps a symbol return that SymbolRef.
/// Otherwise, return 0.
///
@@ -132,22 +126,6 @@ SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
return getAsLocSymbol(IncludeBaseRegions);
}
-/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then
-/// return that expression. Otherwise return NULL.
-const SymExpr *SVal::getAsSymbolicExpression() const {
- if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
- return X->getSymbol();
-
- return getAsSymbol();
-}
-
-const SymExpr* SVal::getAsSymExpr() const {
- const SymExpr* Sym = getAsSymbol();
- if (!Sym)
- Sym = getAsSymbolicExpression();
- return Sym;
-}
-
const MemRegion *SVal::getAsRegion() const {
if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
return X->getRegion();
@@ -175,18 +153,18 @@ bool nonloc::PointerToMember::isNullMemberPointer() const {
return getPTMData().isNull();
}
-const DeclaratorDecl *nonloc::PointerToMember::getDecl() const {
+const NamedDecl *nonloc::PointerToMember::getDecl() const {
const auto PTMD = this->getPTMData();
if (PTMD.isNull())
return nullptr;
- const DeclaratorDecl *DD = nullptr;
- if (PTMD.is<const DeclaratorDecl *>())
- DD = PTMD.get<const DeclaratorDecl *>();
+ const NamedDecl *ND = nullptr;
+ if (PTMD.is<const NamedDecl *>())
+ ND = PTMD.get<const NamedDecl *>();
else
- DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
+ ND = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
- return DD;
+ return ND;
}
//===----------------------------------------------------------------------===//
@@ -203,14 +181,14 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const {
nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
const PTMDataType PTMD = getPTMData();
- if (PTMD.is<const DeclaratorDecl *>())
+ if (PTMD.is<const NamedDecl *>())
return {};
return PTMD.get<const PointerToMemberData *>()->begin();
}
nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
const PTMDataType PTMD = getPTMData();
- if (PTMD.is<const DeclaratorDecl *>())
+ if (PTMD.is<const NamedDecl *>())
return {};
return PTMD.get<const PointerToMemberData *>()->end();
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index 8c2e85601576..f93d04ccd61a 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -14,7 +14,6 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
@@ -32,8 +31,7 @@ class SarifDiagnostics : public PathDiagnosticConsumer {
const LangOptions &LO;
public:
- SarifDiagnostics(AnalyzerOptions &, const std::string &Output,
- const LangOptions &LO)
+ SarifDiagnostics(const std::string &Output, const LangOptions &LO)
: OutputFile(Output), LO(LO) {}
~SarifDiagnostics() override = default;
@@ -48,7 +46,7 @@ public:
} // end anonymous namespace
void ento::createSarifDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Output, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
@@ -56,8 +54,9 @@ void ento::createSarifDiagnosticConsumer(
if (Output.empty())
return;
- C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts()));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, Output, PP, CTU);
+ C.push_back(new SarifDiagnostics(Output, PP.getLangOpts()));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
+ CTU);
}
static StringRef getFileName(const FileEntry &FE) {
@@ -160,9 +159,8 @@ static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc,
assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) &&
"position in file is before column number?");
- bool InvalidBuffer = false;
- const MemoryBuffer *Buf = SM.getBuffer(LocInfo.first, &InvalidBuffer);
- assert(!InvalidBuffer && "got an invalid buffer for the location's file");
+ Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first);
+ assert(Buf && "got an invalid buffer for the location's file");
assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
"token extends past end of buffer?");
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 3709106ad44c..f96974f97dcc 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -57,7 +57,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
// SymIntExprs.
if (!canReasonAbout(Cond)) {
// Just add the constraint to the expression without trying to simplify.
- SymbolRef Sym = Cond.getAsSymExpr();
+ SymbolRef Sym = Cond.getAsSymbol();
assert(Sym);
return assumeSymUnsupported(State, Sym, Assumption);
}
@@ -101,7 +101,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
if (!canReasonAbout(Value)) {
// Just add the constraint to the expression without trying to simplify.
- SymbolRef Sym = Value.getAsSymExpr();
+ SymbolRef Sym = Value.getAsSymbol();
assert(Sym);
return assumeSymInclusiveRange(State, Sym, From, To, InRange);
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 2e269f6a596e..facadaf1225f 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -86,7 +86,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
return makeLocAsInteger(LI->getLoc(), castSize);
}
- if (const SymExpr *se = val.getAsSymbolicExpression()) {
+ if (SymbolRef se = val.getAsSymbol()) {
QualType T = Context.getCanonicalType(se->getType());
// If types are the same or both are integers, ignore the cast.
// FIXME: Remove this hack when we support symbolic truncation/extension.
@@ -1106,19 +1106,28 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
}
SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
- BinaryOperator::Opcode op,
- Loc lhs, NonLoc rhs, QualType resultTy) {
+ BinaryOperator::Opcode op, Loc lhs,
+ NonLoc rhs, QualType resultTy) {
if (op >= BO_PtrMemD && op <= BO_PtrMemI) {
if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) {
if (PTMSV->isNullMemberPointer())
return UndefinedVal();
- if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) {
+
+ auto getFieldLValue = [&](const auto *FD) -> SVal {
SVal Result = lhs;
for (const auto &I : *PTMSV)
Result = StateMgr.getStoreManager().evalDerivedToBase(
- Result, I->getType(),I->isVirtual());
+ Result, I->getType(), I->isVirtual());
+
return state->getLValue(FD, Result);
+ };
+
+ if (const auto *FD = PTMSV->getDeclAs<FieldDecl>()) {
+ return getFieldLValue(FD);
+ }
+ if (const auto *FD = PTMSV->getDeclAs<IndirectFieldDecl>()) {
+ return getFieldLValue(FD);
}
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 6ca7aec9caec..79a8eef30576 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/LLVM.h"
@@ -34,6 +35,12 @@ using namespace ento;
void SymExpr::anchor() {}
+StringRef SymbolConjured::getKindStr() const { return "conj_$"; }
+StringRef SymbolDerived::getKindStr() const { return "derived_$"; }
+StringRef SymbolExtent::getKindStr() const { return "extent_$"; }
+StringRef SymbolMetadata::getKindStr() const { return "meta_$"; }
+StringRef SymbolRegionValue::getKindStr() const { return "reg_$"; }
+
LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
@@ -64,7 +71,7 @@ void SymbolCast::dumpToStream(raw_ostream &os) const {
}
void SymbolConjured::dumpToStream(raw_ostream &os) const {
- os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC"
+ os << getKindStr() << getSymbolID() << '{' << T.getAsString() << ", LC"
<< LCtx->getID();
if (S)
os << ", S" << S->getID(LCtx->getDecl()->getASTContext());
@@ -74,24 +81,24 @@ void SymbolConjured::dumpToStream(raw_ostream &os) const {
}
void SymbolDerived::dumpToStream(raw_ostream &os) const {
- os << "derived_$" << getSymbolID() << '{'
- << getParentSymbol() << ',' << getRegion() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getParentSymbol() << ','
+ << getRegion() << '}';
}
void SymbolExtent::dumpToStream(raw_ostream &os) const {
- os << "extent_$" << getSymbolID() << '{' << getRegion() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getRegion() << '}';
}
void SymbolMetadata::dumpToStream(raw_ostream &os) const {
- os << "meta_$" << getSymbolID() << '{'
- << getRegion() << ',' << T.getAsString() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getRegion() << ','
+ << T.getAsString() << '}';
}
void SymbolData::anchor() {}
void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
- os << "reg_$" << getSymbolID()
- << '<' << getType().getAsString() << ' ' << R << '>';
+ os << getKindStr() << getSymbolID() << '<' << getType().getAsString() << ' '
+ << R << '>';
}
bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const {
@@ -482,7 +489,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
}
bool
-SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
+SymbolReaper::isLive(const Expr *ExprVal, const LocationContext *ELCtx) const {
if (LCtx == nullptr)
return false;
@@ -494,7 +501,8 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return true;
}
- // If no statement is provided, everything is this and parent contexts is live.
+ // If no statement is provided, everything in this and parent contexts is
+ // live.
if (!Loc)
return true;
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
index f4c7e5978e19..ae2bad7ee77c 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -34,20 +34,17 @@ namespace {
/// type to the standard error, or to to compliment many others. Emits detailed
/// diagnostics in textual format for the 'text' output type.
class TextDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
DiagnosticsEngine &DiagEng;
const LangOptions &LO;
- const bool IncludePath = false;
- const bool ShouldEmitAsError = false;
- const bool ApplyFixIts = false;
- const bool ShouldDisplayCheckerName = false;
+ bool ShouldDisplayPathNotes;
public:
- TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO,
- bool ShouldIncludePath, const AnalyzerOptions &AnOpts)
- : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath),
- ShouldEmitAsError(AnOpts.AnalyzerWerror),
- ApplyFixIts(AnOpts.ShouldApplyFixIts),
- ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {}
+ TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
+ DiagnosticsEngine &DiagEng, const LangOptions &LO,
+ bool ShouldDisplayPathNotes)
+ : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO),
+ ShouldDisplayPathNotes(ShouldDisplayPathNotes) {}
~TextDiagnostics() override {}
StringRef getName() const override { return "TextDiagnostics"; }
@@ -56,13 +53,13 @@ public:
bool supportsCrossFileDiagnostics() const override { return true; }
PathGenerationScheme getGenerationScheme() const override {
- return IncludePath ? Minimal : None;
+ return ShouldDisplayPathNotes ? Minimal : None;
}
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override {
unsigned WarnID =
- ShouldEmitAsError
+ DiagOpts.ShouldDisplayWarningsAsErrors
? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
: DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
@@ -72,7 +69,7 @@ public:
auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
ArrayRef<SourceRange> Ranges,
ArrayRef<FixItHint> Fixits) {
- if (!ApplyFixIts) {
+ if (!DiagOpts.ShouldApplyFixIts) {
DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
return;
}
@@ -92,9 +89,10 @@ public:
E = Diags.end();
I != E; ++I) {
const PathDiagnostic *PD = *I;
- std::string WarningMsg =
- (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "")
- .str();
+ std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
+ ? " [" + PD->getCheckerName() + "]"
+ : "")
+ .str();
reportPiece(WarnID, PD->getLocation().asLocation(),
(PD->getShortDescription() + WarningMsg).str(),
@@ -110,7 +108,7 @@ public:
Piece->getFixits());
}
- if (!IncludePath)
+ if (!ShouldDisplayPathNotes)
continue;
// Then, add the path notes if necessary.
@@ -125,7 +123,7 @@ public:
}
}
- if (!ApplyFixIts || Repls.empty())
+ if (Repls.empty())
return;
Rewriter Rewrite(SM, LO);
@@ -139,18 +137,19 @@ public:
} // end anonymous namespace
void ento::createTextPathDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Prefix, const clang::Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
- /*ShouldIncludePath*/ true, AnalyzerOpts));
+ C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
+ PP.getLangOpts(),
+ /*ShouldDisplayPathNotes=*/true));
}
void ento::createTextMinimalPathDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Prefix, const clang::Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
- /*ShouldIncludePath*/ false,
- AnalyzerOpts));
+ C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
+ PP.getLangOpts(),
+ /*ShouldDisplayPathNotes=*/false));
}
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
index 392049e21c6e..f2a19b2ccc90 100644
--- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
+++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -150,7 +150,7 @@ public:
break;
#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
case PD_##NAME: \
- CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU); \
+ CREATEFN(Opts->getDiagOpts(), PathConsumers, OutDir, PP, CTU); \
break;
#include "clang/StaticAnalyzer/Core/Analyses.def"
default:
@@ -476,7 +476,7 @@ void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
static bool isBisonFile(ASTContext &C) {
const SourceManager &SM = C.getSourceManager();
FileID FID = SM.getMainFileID();
- StringRef Buffer = SM.getBuffer(FID)->getBuffer();
+ StringRef Buffer = SM.getBufferOrFake(FID).getBuffer();
if (Buffer.startswith("/* A Bison parser, made by"))
return true;
return false;