Skip to content

Commit cfcf76c

Browse files
committed
[-Wunsafe-buffer-usage] Ignore the FixableGadgets that will not be fixed at an earlier stage
`FixableGadget`s are not always associated with variables that are unsafe (warned). For example, they could be associated with variables whose unsafe operations are suppressed or that are not used in any unsafe operation. Such `FixableGadget`s will not be fixed. Removing these `FixableGadget` as early as possible helps improve the performance and stability of the analysis. Reviewed by: NoQ (Artem Dergachev), t-rasmud (Rashmi Mudduluru) Differential revision: https://reviews.llvm.org/D155524
1 parent c1ce7c8 commit cfcf76c

File tree

2 files changed

+180
-10
lines changed

2 files changed

+180
-10
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,16 +1090,6 @@ findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler,
10901090
}
10911091

10921092
M.match(*D->getBody(), D->getASTContext());
1093-
1094-
// Gadgets "claim" variables they're responsible for. Once this loop finishes,
1095-
// the tracker will only track DREs that weren't claimed by any gadgets,
1096-
// i.e. not understood by the analysis.
1097-
for (const auto &G : CB.FixableGadgets) {
1098-
for (const auto *DRE : G->getClaimedVarUseSites()) {
1099-
CB.Tracker.claimUse(DRE);
1100-
}
1101-
}
1102-
11031093
return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
11041094
std::move(CB.Tracker)};
11051095
}
@@ -2250,6 +2240,33 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
22502240
return;
22512241
}
22522242

2243+
// If no `WarningGadget`s ever matched, there is no unsafe operations in the
2244+
// function under the analysis. No need to fix any Fixables.
2245+
if (!WarningGadgets.empty()) {
2246+
// Gadgets "claim" variables they're responsible for. Once this loop
2247+
// finishes, the tracker will only track DREs that weren't claimed by any
2248+
// gadgets, i.e. not understood by the analysis.
2249+
for (const auto &G : FixableGadgets) {
2250+
for (const auto *DRE : G->getClaimedVarUseSites()) {
2251+
Tracker.claimUse(DRE);
2252+
}
2253+
}
2254+
}
2255+
2256+
// If no `WarningGadget`s ever matched, there is no unsafe operations in the
2257+
// function under the analysis. Thus, it early returns here as there is
2258+
// nothing needs to be fixed.
2259+
//
2260+
// Note this claim is based on the assumption that there is no unsafe
2261+
// variable whose declaration is invisible from the analyzing function.
2262+
// Otherwise, we need to consider if the uses of those unsafe varuables needs
2263+
// fix.
2264+
// So far, we are not fixing any global variables or class members. And,
2265+
// lambdas will be analyzed along with the enclosing function. So this early
2266+
// return is correct for now.
2267+
if (WarningGadgets.empty())
2268+
return;
2269+
22532270
UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
22542271
FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
22552272

@@ -2356,6 +2373,34 @@ void clang::checkUnsafeBufferUsage(const Decl *D,
23562373
}
23572374
}
23582375

2376+
// Remove a `FixableGadget` if the associated variable is not in the graph
2377+
// computed above. We do not want to generate fix-its for such variables,
2378+
// since they are neither warned nor reachable from a warned one.
2379+
//
2380+
// Note a variable is not warned if it is not directly used in any unsafe
2381+
// operation. A variable `v` is NOT reachable from an unsafe variable, if it
2382+
// does not exist another variable `u` such that `u` is warned and fixing `u`
2383+
// (transitively) implicates fixing `v`.
2384+
//
2385+
// For example,
2386+
// ```
2387+
// void f(int * p) {
2388+
// int * a = p; *p = 0;
2389+
// }
2390+
// ```
2391+
// `*p = 0` is a fixable gadget associated with a variable `p` that is neither
2392+
// warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
2393+
// the function above, `p` becomes reachable from a warned variable.
2394+
for (auto I = FixablesForAllVars.byVar.begin();
2395+
I != FixablesForAllVars.byVar.end();) {
2396+
// Note `VisitedVars` contain all the variables in the graph:
2397+
if (VisitedVars.find((*I).first) == VisitedVars.end()) {
2398+
// no such var in graph:
2399+
I = FixablesForAllVars.byVar.erase(I);
2400+
} else
2401+
++I;
2402+
}
2403+
23592404
Strategy NaiveStrategy = getNaiveStrategy(UnsafeVars);
23602405

23612406
FixItsForVariableGroup =

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

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,128 @@ void noteGoesWithVarDeclWarning() {
107107

108108
p[5]; // not to note since the associated warning is suppressed
109109
}
110+
111+
112+
// Test suppressing interacts with variable grouping:
113+
114+
// The implication edges are: `a` -> `b` -> `c`.
115+
// If the unsafe operation on `a` is supressed, none of the variables
116+
// will be fixed.
117+
void suppressedVarInGroup() {
118+
int * a;
119+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
120+
int * b;
121+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
122+
int * c;
123+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
124+
125+
#pragma clang unsafe_buffer_usage begin
126+
a[5] = 5;
127+
#pragma clang unsafe_buffer_usage end
128+
a = b;
129+
b = c;
130+
}
131+
132+
// To show that if `a[5]` is not suppressed in the
133+
// `suppressedVarInGroup` function above, all variables will be fixed.
134+
void suppressedVarInGroup_control() {
135+
int * a;
136+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> a"
137+
int * b;
138+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> b"
139+
int * c;
140+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> c"
141+
142+
a[5] = 5;
143+
a = b;
144+
b = c;
145+
}
146+
147+
// The implication edges are: `a` -> `b` -> `c`.
148+
// The unsafe operation on `b` is supressed, while the unsafe
149+
// operation on `a` is not suppressed. Variable `b` will still be
150+
// fixed when fixing `a`.
151+
void suppressedVarInGroup2() {
152+
int * a;
153+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> a"
154+
int * b;
155+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> b"
156+
int * c;
157+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> c"
158+
159+
a[5] = 5;
160+
#pragma clang unsafe_buffer_usage begin
161+
b[5] = 5;
162+
#pragma clang unsafe_buffer_usage end
163+
a = b;
164+
b = c;
165+
}
166+
167+
// The implication edges are: `a` -> `b` -> `c`.
168+
// The unsafe operation on `b` is supressed, while the unsafe
169+
// operation on `c` is not suppressed. Only variable `c` will be fixed
170+
// then.
171+
void suppressedVarInGroup3() {
172+
int * a;
173+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
174+
int * b;
175+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
176+
int * c;
177+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> c"
178+
179+
c[5] = 5;
180+
#pragma clang unsafe_buffer_usage begin
181+
b[5] = 5;
182+
#pragma clang unsafe_buffer_usage end
183+
a = b;
184+
b = c;
185+
}
186+
187+
// The implication edges are: `a` -> `b` -> `c` -> `a`.
188+
// The unsafe operation on `b` is supressed, while the unsafe
189+
// operation on `c` is not suppressed. Since the implication graph
190+
// forms a cycle, all variables will be fixed.
191+
void suppressedVarInGroup4() {
192+
int * a;
193+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> a"
194+
int * b;
195+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> b"
196+
int * c;
197+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> c"
198+
199+
c[5] = 5;
200+
#pragma clang unsafe_buffer_usage begin
201+
b[5] = 5;
202+
#pragma clang unsafe_buffer_usage end
203+
a = b;
204+
b = c;
205+
c = a;
206+
}
207+
208+
// There are two groups: `a` -> `b` -> `c` and `d` -> `e` -> `f`.
209+
// Suppressing unsafe operations on variables in one group does not
210+
// affect other groups.
211+
void suppressedVarInGroup5() {
212+
int * a;
213+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
214+
int * b;
215+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
216+
int * c;
217+
// CHECK-NOT: fix-it:"{{.*}}":{[[@LINE-1]]:
218+
int * d;
219+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> d"
220+
int * e;
221+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> e"
222+
int * f;
223+
// CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:10}:"std::span<int> f"
224+
225+
#pragma clang unsafe_buffer_usage begin
226+
a[5] = 5;
227+
#pragma clang unsafe_buffer_usage end
228+
a = b;
229+
b = c;
230+
231+
d[5] = 5;
232+
d = e;
233+
e = f;
234+
}

0 commit comments

Comments
 (0)