Skip to content

Commit ef6f833

Browse files
[-Wunsafe-buffer-usage] Check missing and duplicated assignments
Add checks for missing and duplicated assignments in bounds-attributed groups. This model requires each mutable decl in a group to be assigned exactly once. rdar://161608243
1 parent 7d6bb97 commit ef6f833

File tree

5 files changed

+252
-1
lines changed

5 files changed

+252
-1
lines changed

clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,22 @@ class UnsafeBufferUsageHandler {
165165
ASTContext &Ctx) {
166166
handleUnsafeOperation(Assign, IsRelatedToDecl, Ctx);
167167
}
168+
169+
virtual void handleMissingAssignments(
170+
const Expr *LastAssignInGroup,
171+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Required,
172+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Missing,
173+
bool IsRelatedToDecl, ASTContext &Ctx) {
174+
handleUnsafeOperation(LastAssignInGroup, IsRelatedToDecl, Ctx);
175+
}
176+
177+
virtual void handleDuplicatedAssignment(const BinaryOperator *Assign,
178+
const BinaryOperator *PrevAssign,
179+
const ValueDecl *VD,
180+
bool IsRelatedToDecl,
181+
ASTContext &Ctx) {
182+
handleUnsafeOperation(Assign, IsRelatedToDecl, Ctx);
183+
}
168184
/* TO_UPSTREAM(BoundsSafety) OFF */
169185

170186
/// Invoked when a fix is suggested against a variable. This function groups

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14283,6 +14283,12 @@ def warn_cannot_assign_to_immutable_bounds_attributed_object : Warning<
1428314283
"its type depends on an inout dependent count|"
1428414284
"it's used as dependent count in an inout count-attributed pointer"
1428514285
"}2">, InGroup<UnsafeBufferUsage>, DefaultIgnore;
14286+
def warn_missing_assignments_in_bounds_attributed_group : Warning<
14287+
"bounds-attributed group requires assigning '%0', assignments to '%1' missing">,
14288+
InGroup<UnsafeBufferUsage>, DefaultIgnore;
14289+
def warn_duplicated_assignment_in_bounds_attributed_group : Warning<
14290+
"duplicated assignment to %select{variable|parameter|member}0 %1 in "
14291+
"bounds-attributed group">, InGroup<UnsafeBufferUsage>, DefaultIgnore;
1428614292
#ifndef NDEBUG
1428714293
// Not a user-facing diagnostic. Useful for debugging false negatives in
1428814294
// -fsafe-buffer-usage-suggestions (i.e. lack of -Wunsafe-buffer-usage fixits).

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5889,14 +5889,73 @@ static bool checkImmutableBoundsAttributedObjects(
58895889
return IsGroupSafe;
58905890
}
58915891

5892+
// Checks if the bounds-attributed group has missing or duplicated assignments.
5893+
// Each assignable decl in the group must be assigned exactly once.
5894+
//
5895+
// For example:
5896+
// void foo(int *__counted_by(a + b) p, int a, int b) {
5897+
// p = ...;
5898+
// p = ...; // duplicated
5899+
// a = ...;
5900+
// // b missing
5901+
// }
5902+
// Note that the group consists of a, b, and p.
5903+
//
5904+
// The function returns true iff each assignable decl in the group is assigned
5905+
// exactly once.
5906+
static bool checkMissingAndDuplicatedAssignments(
5907+
const BoundsAttributedAssignmentGroup &Group,
5908+
UnsafeBufferUsageHandler &Handler, ASTContext &Ctx) {
5909+
llvm::SmallDenseMap<const ValueDecl *, const BinaryOperator *, 4>
5910+
AssignedDecls;
5911+
5912+
DependentDeclSetTy RequiredDecls = Group.DeclClosure;
5913+
for (const ValueDecl *VD : Group.DeclClosure) {
5914+
if (VD->isDependentCountWithoutDeref() &&
5915+
VD->isDependentCountThatIsUsedInInoutPointer()) {
5916+
// This value is immutable, so it's not required to be assigned.
5917+
RequiredDecls.erase(VD);
5918+
}
5919+
}
5920+
5921+
DependentDeclSetTy MissingDecls = RequiredDecls;
5922+
5923+
bool IsGroupSafe = true;
5924+
5925+
for (size_t I = 0, N = Group.Assignments.size(); I < N; ++I) {
5926+
const BinaryOperator *Assign = Group.Assignments[I];
5927+
const ValueDecl *VD = Group.AssignedObjects[I].Decl;
5928+
5929+
const auto [It, Inserted] = AssignedDecls.insert({VD, Assign});
5930+
if (Inserted)
5931+
MissingDecls.erase(VD);
5932+
else {
5933+
const BinaryOperator *PrevAssign = It->second;
5934+
Handler.handleDuplicatedAssignment(Assign, PrevAssign, VD,
5935+
/*IsRelatedToDecl=*/false, Ctx);
5936+
IsGroupSafe = false;
5937+
}
5938+
}
5939+
5940+
if (!MissingDecls.empty()) {
5941+
const Expr *LastAssign = Group.Assignments.back();
5942+
Handler.handleMissingAssignments(LastAssign, RequiredDecls, MissingDecls,
5943+
/*IsRelatedToDecl=*/false, Ctx);
5944+
IsGroupSafe = false;
5945+
}
5946+
5947+
return IsGroupSafe;
5948+
}
5949+
58925950
// Checks if the bounds-attributed group is safe. This function returns false
58935951
// iff the assignment group is unsafe and diagnostics have been emitted.
58945952
static bool
58955953
checkBoundsAttributedGroup(const BoundsAttributedAssignmentGroup &Group,
58965954
UnsafeBufferUsageHandler &Handler, ASTContext &Ctx) {
58975955
if (!checkImmutableBoundsAttributedObjects(Group, Handler, Ctx))
58985956
return false;
5899-
5957+
if (!checkMissingAndDuplicatedAssignments(Group, Handler, Ctx))
5958+
return false;
59005959
// TODO: Add more checks.
59015960
return true;
59025961
}

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2643,6 +2643,48 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
26432643
diag::warn_cannot_assign_to_immutable_bounds_attributed_object)
26442644
<< getBoundsAttributedObjectKind(VD) << VD << Kind;
26452645
}
2646+
2647+
static llvm::SmallString<64>
2648+
DeclSetToStr(const llvm::SmallPtrSetImpl<const ValueDecl *> &Set) {
2649+
llvm::SmallVector<const ValueDecl *, 4> V(Set.begin(), Set.end());
2650+
llvm::sort(V.begin(), V.end(), [](const ValueDecl *A, const ValueDecl *B) {
2651+
return A->getName().compare(B->getName()) < 0;
2652+
});
2653+
llvm::SmallString<64> Str;
2654+
for (const ValueDecl *VD : V) {
2655+
if (!Str.empty())
2656+
Str += ", ";
2657+
Str += VD->getName();
2658+
}
2659+
return Str;
2660+
}
2661+
2662+
void handleMissingAssignments(
2663+
const Expr *LastAssignInGroup,
2664+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Required,
2665+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Missing,
2666+
bool IsRelatedToDecl, ASTContext &Ctx) override {
2667+
2668+
llvm::SmallString<64> RequiredAssignments = DeclSetToStr(Required);
2669+
llvm::SmallString<64> MissingAssignments = DeclSetToStr(Missing);
2670+
auto Loc =
2671+
CharSourceRange::getTokenRange(LastAssignInGroup->getSourceRange())
2672+
.getEnd();
2673+
2674+
S.Diag(Loc, diag::warn_missing_assignments_in_bounds_attributed_group)
2675+
<< RequiredAssignments << MissingAssignments;
2676+
}
2677+
2678+
void handleDuplicatedAssignment(const BinaryOperator *Assign,
2679+
const BinaryOperator *PrevAssign,
2680+
const ValueDecl *VD, bool IsRelatedToDecl,
2681+
ASTContext &Ctx) override {
2682+
S.Diag(Assign->getOperatorLoc(),
2683+
diag::warn_duplicated_assignment_in_bounds_attributed_group)
2684+
<< getBoundsAttributedObjectKind(VD) << VD;
2685+
S.Diag(PrevAssign->getOperatorLoc(),
2686+
diag::note_bounds_safety_previous_assignment);
2687+
}
26462688
/* TO_UPSTREAM(BoundsSafety) OFF */
26472689

26482690
void handleUnsafeVariableGroup(const VarDecl *Variable,

clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-assignment.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,134 @@ class immutable_class {
211211
}
212212
};
213213

214+
// Missing assignments
215+
216+
void missing_ptr(int *__counted_by(count) p, int count) {
217+
count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
218+
}
219+
220+
void missing_count(int *__counted_by(count) p, int count) {
221+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
222+
}
223+
224+
void missing_structure(int *__counted_by(count) p, int count) {
225+
{
226+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
227+
}
228+
{
229+
count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
230+
}
231+
}
232+
233+
void missing_structure2(int *__counted_by(count) p, int count) {
234+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
235+
{
236+
count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
237+
}
238+
}
239+
240+
void missing_structure3(int *__counted_by(count) p, int count) {
241+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
242+
if (count > 0) {
243+
count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
244+
}
245+
}
246+
247+
void missing_unrelated(int *__counted_by(count) p, int count, int *__counted_by(len) q, int len) {
248+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
249+
len = 0; // expected-warning{{bounds-attributed group requires assigning 'len, q', assignments to 'q' missing}}
250+
}
251+
252+
void missing_complex_count1(int *__counted_by(a + b) p, int a, int b) {
253+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'a, b, p', assignments to 'a, b' missing}}
254+
}
255+
256+
void missing_complex_count2(int *__counted_by(a + b) p, int a, int b) {
257+
p = nullptr;
258+
a = 0; // expected-warning{{bounds-attributed group requires assigning 'a, b, p', assignments to 'b' missing}}
259+
}
260+
261+
void missing_complex_count3(int *__counted_by(a + b) p, int a, int b) {
262+
b = 0;
263+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'a, b, p', assignments to 'a' missing}}
264+
}
265+
266+
void missing_complex_count4(int *__counted_by(a + b) p, int a, int b) {
267+
a = 0;
268+
b = 0; // expected-warning{{bounds-attributed group requires assigning 'a, b, p', assignments to 'p' missing}}
269+
}
270+
271+
void missing_complex_ptr1(int *__counted_by(count) p, int *__counted_by(count) q, int count) {
272+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p, q', assignments to 'count, q' missing}}
273+
}
274+
275+
void missing_complex_ptr2(int *__counted_by(count) p, int *__counted_by(count) q, int count) {
276+
p = nullptr;
277+
q = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p, q', assignments to 'count' missing}}
278+
}
279+
280+
void missing_complex_ptr3(int *__counted_by(count) p, int *__counted_by(count) q, int count) {
281+
count = 0;
282+
p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p, q', assignments to 'q' missing}}
283+
}
284+
285+
void missing_complex_ptr4(int *__counted_by(count) p, int *__counted_by(count) q, int count) {
286+
q = nullptr;
287+
count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p, q', assignments to 'p' missing}}
288+
}
289+
290+
// Missing assignments in struct
291+
292+
void missing_struct_ptr(cb *c) {
293+
c->count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
294+
}
295+
296+
void missing_struct_count(cb *c) {
297+
c->p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
298+
}
299+
300+
void missing_struct_unrelated(cb *c, cb *d) {
301+
c->p = nullptr; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'count' missing}}
302+
d->count = 0; // expected-warning{{bounds-attributed group requires assigning 'count, p', assignments to 'p' missing}}
303+
}
304+
305+
// Duplicated assignments
306+
307+
void duplicated_ptr(int *__counted_by(count) p, int count) {
308+
p = nullptr; // expected-note{{previously assigned here}}
309+
p = nullptr; // expected-warning{{duplicated assignment to parameter 'p' in bounds-attributed group}}
310+
count = 0;
311+
}
312+
313+
void duplicated_ptr2(int *__counted_by(count) p, int count) {
314+
p = nullptr; // expected-note{{previously assigned here}}
315+
count = 0;
316+
p = nullptr; // expected-warning{{duplicated assignment to parameter 'p' in bounds-attributed group}}
317+
}
318+
319+
void duplicated_count(int *__counted_by(count) p, int count) {
320+
p = nullptr;
321+
count = 0; // expected-note{{previously assigned here}}
322+
count = 0; // expected-warning{{duplicated assignment to parameter 'count' in bounds-attributed group}}
323+
}
324+
325+
void duplicated_count2(int *__counted_by(count) p, int count) {
326+
count = 0; // expected-note{{previously assigned here}}
327+
p = nullptr;
328+
count = 0; // expected-warning{{duplicated assignment to parameter 'count' in bounds-attributed group}}
329+
}
330+
331+
void duplicated_complex(int *__counted_by(a + b) p,
332+
int *__counted_by(a + b + c) q,
333+
int a, int b, int c) {
334+
p = nullptr;
335+
q = nullptr; // expected-note{{previously assigned here}}
336+
a = 0;
337+
b = 0;
338+
c = 0;
339+
q = nullptr; // expected-warning{{duplicated assignment to parameter 'q' in bounds-attributed group}}
340+
}
341+
214342
// Assigns to bounds-attributed that we consider too complex to analyze.
215343

216344
void too_complex_assign_to_ptr(int *__counted_by(count) p, int count, int *q) {

0 commit comments

Comments
 (0)