Skip to content

Commit 7d0d34f

Browse files
committed
Re-land "[-Wunsafe-buffer-usage] Add a new forEachDescendant matcher that skips callable declarations"
This reverts commit 22df454. After a quick investigation, realizing that the Sanitizer test failures caused by this patch is not likely to block other contributors. I re-land this patch before taking a closer look at those tests so that it won't block the [-Wunsafe-buffer-usage] development.
1 parent 85bff00 commit 7d0d34f

File tree

2 files changed

+167
-5
lines changed

2 files changed

+167
-5
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,112 @@
88

99
#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
1010
#include "clang/ASTMatchers/ASTMatchFinder.h"
11+
#include "clang/AST/RecursiveASTVisitor.h"
1112
#include "llvm/ADT/SmallVector.h"
1213

1314
using namespace llvm;
1415
using namespace clang;
1516
using namespace ast_matchers;
1617

18+
namespace clang::ast_matchers {
19+
// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
20+
// except for those belonging to a different callable of "n".
21+
class MatchDescendantVisitor
22+
: public RecursiveASTVisitor<MatchDescendantVisitor> {
23+
public:
24+
typedef RecursiveASTVisitor<MatchDescendantVisitor> VisitorBase;
25+
26+
// Creates an AST visitor that matches `Matcher` on all
27+
// descendants of a given node "n" except for the ones
28+
// belonging to a different callable of "n".
29+
MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
30+
internal::ASTMatchFinder *Finder,
31+
internal::BoundNodesTreeBuilder *Builder,
32+
internal::ASTMatchFinder::BindKind Bind)
33+
: Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
34+
Matches(false) {}
35+
36+
// Returns true if a match is found in a subtree of `DynNode`, which belongs
37+
// to the same callable of `DynNode`.
38+
bool findMatch(const DynTypedNode &DynNode) {
39+
Matches = false;
40+
if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
41+
TraverseStmt(const_cast<Stmt *>(StmtNode));
42+
*Builder = ResultBindings;
43+
return Matches;
44+
}
45+
return false;
46+
}
47+
48+
// The following are overriding methods from the base visitor class.
49+
// They are public only to allow CRTP to work. They are *not *part
50+
// of the public API of this class.
51+
52+
// For the matchers so far used in safe buffers, we only need to match
53+
// `Stmt`s. To override more as needed.
54+
55+
bool TraverseDecl(Decl *Node) {
56+
if (!Node)
57+
return true;
58+
if (!match(*Node))
59+
return false;
60+
// To skip callables:
61+
if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
62+
return true;
63+
// Traverse descendants
64+
return VisitorBase::TraverseDecl(Node);
65+
}
66+
67+
bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
68+
if (!Node)
69+
return true;
70+
if (!match(*Node))
71+
return false;
72+
// To skip callables:
73+
if (isa<LambdaExpr>(Node))
74+
return true;
75+
return VisitorBase::TraverseStmt(Node);
76+
}
77+
78+
bool shouldVisitTemplateInstantiations() const { return true; }
79+
bool shouldVisitImplicitCode() const {
80+
// TODO: let's ignore implicit code for now
81+
return false;
82+
}
83+
84+
private:
85+
// Sets 'Matched' to true if 'Matcher' matches 'Node'
86+
//
87+
// Returns 'true' if traversal should continue after this function
88+
// returns, i.e. if no match is found or 'Bind' is 'BK_All'.
89+
template <typename T> bool match(const T &Node) {
90+
internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
91+
92+
if (Matcher->matches(DynTypedNode::create(Node), Finder,
93+
&RecursiveBuilder)) {
94+
ResultBindings.addMatch(RecursiveBuilder);
95+
Matches = true;
96+
if (Bind != internal::ASTMatchFinder::BK_All)
97+
return false; // Abort as soon as a match is found.
98+
}
99+
return true;
100+
}
101+
102+
const internal::DynTypedMatcher *const Matcher;
103+
internal::ASTMatchFinder *const Finder;
104+
internal::BoundNodesTreeBuilder *const Builder;
105+
internal::BoundNodesTreeBuilder ResultBindings;
106+
const internal::ASTMatchFinder::BindKind Bind;
107+
bool Matches;
108+
};
109+
110+
AST_MATCHER_P(Stmt, forEveryDescendant, internal::Matcher<Stmt>, innerMatcher) {
111+
MatchDescendantVisitor Visitor(new DynTypedMatcher(innerMatcher), Finder,
112+
Builder, ASTMatchFinder::BK_All);
113+
return Visitor.findMatch(DynTypedNode::create(Node));
114+
}
115+
} // namespace clang::ast_matchers
116+
17117
namespace {
18118
// Because the analysis revolves around variables and their types, we'll need to
19119
// track uses of variables (aka DeclRefExprs).
@@ -398,7 +498,7 @@ static std::pair<GadgetList, DeclUseTracker> findGadgets(const Decl *D) {
398498

399499
// clang-format off
400500
M.addMatcher(
401-
stmt(forEachDescendant(
501+
stmt(forEveryDescendant(
402502
stmt(anyOf(
403503
// Add Gadget::matcher() for every gadget in the registry.
404504
#define GADGET(x) \

clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -include %s -verify %s
1+
// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -fblocks -include %s -verify %s
22
#ifndef INCLUDED
33
#define INCLUDED
44
#pragma clang system_header
@@ -141,15 +141,15 @@ void testStructMembers(struct T * sp, struct T s, T_t * sp2, T_t s2) {
141141

142142
int garray[10];
143143
int * gp = garray;
144-
int gvar = gp[1]; // TODO: this is not warned
144+
int gvar = gp[1]; // FIXME: file scope unsafe buffer access is not warned
145145

146146
void testLambdaCaptureAndGlobal(int * p) {
147147
int a[10];
148148

149149
auto Lam = [p, a]() {
150-
return p[1] // expected-warning2{{unchecked operation on raw buffer in expression}}
150+
return p[1] // expected-warning{{unchecked operation on raw buffer in expression}}
151151
+ a[1] + garray[1]
152-
+ gp[1]; // expected-warning2{{unchecked operation on raw buffer in expression}}
152+
+ gp[1]; // expected-warning{{unchecked operation on raw buffer in expression}}
153153
};
154154
}
155155

@@ -213,4 +213,66 @@ void testPointerToMember() {
213213
foo(S.*p,
214214
(S.*q)[1]); // expected-warning{{unchecked operation on raw buffer in expression}}
215215
}
216+
217+
// test that nested callable definitions are scanned only once
218+
void testNestedCallableDefinition(int * p) {
219+
class A {
220+
void inner(int * p) {
221+
p++; // expected-warning{{unchecked operation on raw buffer in expression}}
222+
}
223+
224+
static void innerStatic(int * p) {
225+
p++; // expected-warning{{unchecked operation on raw buffer in expression}}
226+
}
227+
228+
void innerInner(int * p) {
229+
auto Lam = [p]() {
230+
int * q = p;
231+
q++; // expected-warning{{unchecked operation on raw buffer in expression}}
232+
return *q;
233+
};
234+
}
235+
};
236+
237+
auto Lam = [p]() {
238+
int * q = p;
239+
q++; // expected-warning{{unchecked operation on raw buffer in expression}}
240+
return *q;
241+
};
242+
243+
auto LamLam = [p]() {
244+
auto Lam = [p]() {
245+
int * q = p;
246+
q++; // expected-warning{{unchecked operation on raw buffer in expression}}
247+
return *q;
248+
};
249+
};
250+
251+
void (^Blk)(int*) = ^(int *p) {
252+
p++; // expected-warning{{unchecked operation on raw buffer in expression}}
253+
};
254+
255+
void (^BlkBlk)(int*) = ^(int *p) {
256+
void (^Blk)(int*) = ^(int *p) {
257+
p++; // expected-warning{{unchecked operation on raw buffer in expression}}
258+
};
259+
Blk(p);
260+
};
261+
262+
// lambda and block as call arguments...
263+
foo( [p]() { int * q = p;
264+
q++; // expected-warning{{unchecked operation on raw buffer in expression}}
265+
return *q;
266+
},
267+
^(int *p) { p++; // expected-warning{{unchecked operation on raw buffer in expression}}
268+
}
269+
);
270+
}
271+
272+
void testVariableDecls(int * p) {
273+
int * q = p++; // expected-warning{{unchecked operation on raw buffer in expression}}
274+
int a[p[1]]; // expected-warning{{unchecked operation on raw buffer in expression}}
275+
int b = p[1]; // expected-warning{{unchecked operation on raw buffer in expression}}
276+
}
277+
216278
#endif

0 commit comments

Comments
 (0)