Skip to content

Commit 8c155f4

Browse files
authored
Merge pull request #1835 from JDevlieghere/merge-bastille-into-swift-master
Merge bastille into swift main
2 parents ba3adbb + f0ddcab commit 8c155f4

File tree

61 files changed

+1851
-519
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1851
-519
lines changed

clang/docs/analyzer/checkers.rst

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,25 @@ Raw pointers and references to uncounted types can't be used as class members. O
14231423
// ...
14241424
};
14251425
1426+
.. _webkit-UncountedLambdaCapturesChecker:
1427+
1428+
webkit.UncountedLambdaCapturesChecker
1429+
"""""""""""""""""""""""""""""""""""""
1430+
Raw pointers and references to uncounted types can't be captured in lambdas. Only ref-counted types are allowed.
1431+
1432+
.. code-block:: cpp
1433+
1434+
struct RefCntbl {
1435+
void ref() {}
1436+
void deref() {}
1437+
};
1438+
1439+
void foo(RefCntbl* a, RefCntbl& b) {
1440+
[&, a](){ // warn about 'a'
1441+
do_something(b); // warn about 'b'
1442+
};
1443+
};
1444+
14261445
.. _alpha-checkers:
14271446
14281447
Experimental Checkers
@@ -2519,6 +2538,53 @@ We also define a set of safe transformations which if passed a safe value as an
25192538
- casts
25202539
- unary operators like ``&`` or ``*``
25212540
2541+
alpha.webkit.UncountedLocalVarsChecker
2542+
""""""""""""""""""""""""""""""""""""""
2543+
The goal of this rule is to make sure that any uncounted local variable is backed by a ref-counted object with lifetime that is strictly larger than the scope of the uncounted local variable. To be on the safe side we require the scope of an uncounted variable to be embedded in the scope of ref-counted object that backs it.
2544+
2545+
These are examples of cases that we consider safe:
2546+
2547+
.. code-block:: cpp
2548+
void foo1() {
2549+
RefPtr<RefCountable> counted;
2550+
// The scope of uncounted is EMBEDDED in the scope of counted.
2551+
{
2552+
RefCountable* uncounted = counted.get(); // ok
2553+
}
2554+
}
2555+
2556+
void foo2(RefPtr<RefCountable> counted_param) {
2557+
RefCountable* uncounted = counted_param.get(); // ok
2558+
}
2559+
2560+
void FooClass::foo_method() {
2561+
RefCountable* uncounted = this; // ok
2562+
}
2563+
2564+
Here are some examples of situations that we warn about as they *might* be potentially unsafe. The logic is that either we're able to guarantee that an argument is safe or it's considered if not a bug then bug-prone.
2565+
2566+
.. code-block:: cpp
2567+
2568+
void foo1() {
2569+
RefCountable* uncounted = new RefCountable; // warn
2570+
}
2571+
2572+
RefCountable* global_uncounted;
2573+
void foo2() {
2574+
RefCountable* uncounted = global_uncounted; // warn
2575+
}
2576+
2577+
void foo3() {
2578+
RefPtr<RefCountable> counted;
2579+
// The scope of uncounted is not EMBEDDED in the scope of counted.
2580+
RefCountable* uncounted = counted.get(); // warn
2581+
}
2582+
2583+
We don't warn about these cases - we don't consider them necessarily safe but since they are very common and usually safe we'd introduce a lot of false positives otherwise:
2584+
- variable defined in condition part of an ```if``` statement
2585+
- variable defined in init statement condition of a ```for``` statement
2586+
2587+
For the time being we also don't warn about uninitialized uncounted local variables.
25222588
25232589
Debug Checkers
25242590
---------------

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,10 @@ def NoUncountedMemberChecker : Checker<"NoUncountedMemberChecker">,
16541654
HelpText<"Check for no uncounted member variables.">,
16551655
Documentation<HasDocumentation>;
16561656

1657+
def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
1658+
HelpText<"Check uncounted lambda captures.">,
1659+
Documentation<HasDocumentation>;
1660+
16571661
} // end webkit
16581662

16591663
let ParentPackage = WebKitAlpha in {
@@ -1662,4 +1666,8 @@ def UncountedCallArgsChecker : Checker<"UncountedCallArgsChecker">,
16621666
HelpText<"Check uncounted call arguments.">,
16631667
Documentation<HasDocumentation>;
16641668

1669+
def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">,
1670+
HelpText<"Check uncounted local variables.">,
1671+
Documentation<HasDocumentation>;
1672+
16651673
} // end alpha.webkit

clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ add_clang_library(clangStaticAnalyzerCheckers
127127
WebKit/PtrTypesSemantics.cpp
128128
WebKit/RefCntblBaseVirtualDtorChecker.cpp
129129
WebKit/UncountedCallArgsChecker.cpp
130+
WebKit/UncountedLambdaCapturesChecker.cpp
131+
WebKit/UncountedLocalVarsChecker.cpp
130132

131133
LINK_LIBS
132134
clangAST
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
//=======- UncountedLambdaCapturesChecker.cpp --------------------*- C++ -*-==//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DiagOutputUtils.h"
10+
#include "PtrTypesSemantics.h"
11+
#include "clang/AST/CXXInheritance.h"
12+
#include "clang/AST/RecursiveASTVisitor.h"
13+
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14+
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
15+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16+
#include "clang/StaticAnalyzer/Core/Checker.h"
17+
18+
using namespace clang;
19+
using namespace ento;
20+
21+
namespace {
22+
class UncountedLambdaCapturesChecker
23+
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
24+
private:
25+
BugType Bug{this, "Lambda capture of uncounted variable",
26+
"WebKit coding guidelines"};
27+
mutable BugReporter *BR;
28+
29+
public:
30+
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
31+
BugReporter &BRArg) const {
32+
BR = &BRArg;
33+
34+
// The calls to checkAST* from AnalysisConsumer don't
35+
// visit template instantiations or lambda classes. We
36+
// want to visit those, so we make our own RecursiveASTVisitor.
37+
struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
38+
const UncountedLambdaCapturesChecker *Checker;
39+
explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
40+
: Checker(Checker) {
41+
assert(Checker);
42+
}
43+
44+
bool shouldVisitTemplateInstantiations() const { return true; }
45+
bool shouldVisitImplicitCode() const { return false; }
46+
47+
bool VisitLambdaExpr(LambdaExpr *L) {
48+
Checker->visitLambdaExpr(L);
49+
return true;
50+
}
51+
};
52+
53+
LocalVisitor visitor(this);
54+
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
55+
}
56+
57+
void visitLambdaExpr(LambdaExpr *L) const {
58+
for (const LambdaCapture &C : L->captures()) {
59+
if (C.capturesVariable()) {
60+
VarDecl *CapturedVar = C.getCapturedVar();
61+
if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
62+
if (isUncountedPtr(CapturedVarType)) {
63+
reportBug(C, CapturedVar, CapturedVarType);
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
void reportBug(const LambdaCapture &Capture, VarDecl *CapturedVar,
71+
const Type *T) const {
72+
assert(CapturedVar);
73+
74+
SmallString<100> Buf;
75+
llvm::raw_svector_ostream Os(Buf);
76+
77+
if (Capture.isExplicit()) {
78+
Os << "Captured ";
79+
} else {
80+
Os << "Implicitly captured ";
81+
}
82+
if (T->isPointerType()) {
83+
Os << "raw-pointer ";
84+
} else {
85+
assert(T->isReferenceType());
86+
Os << "reference ";
87+
}
88+
89+
printQuotedQualifiedName(Os, Capture.getCapturedVar());
90+
Os << " to uncounted type is unsafe.";
91+
92+
PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
93+
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
94+
BR->emitReport(std::move(Report));
95+
}
96+
};
97+
} // namespace
98+
99+
void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
100+
Mgr.registerChecker<UncountedLambdaCapturesChecker>();
101+
}
102+
103+
bool ento::shouldRegisterUncountedLambdaCapturesChecker(
104+
const CheckerManager &mgr) {
105+
return true;
106+
}

0 commit comments

Comments
 (0)