Skip to content

Commit 1f484c9

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 (cherry picked from commit ef6f833)
1 parent ffa9ca8 commit 1f484c9

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
@@ -164,6 +164,22 @@ class UnsafeBufferUsageHandler {
164164
ASTContext &Ctx) {
165165
handleUnsafeOperation(Assign, IsRelatedToDecl, Ctx);
166166
}
167+
168+
virtual void handleMissingAssignments(
169+
const Expr *LastAssignInGroup,
170+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Required,
171+
const llvm::SmallPtrSetImpl<const ValueDecl *> &Missing,
172+
bool IsRelatedToDecl, ASTContext &Ctx) {
173+
handleUnsafeOperation(LastAssignInGroup, IsRelatedToDecl, Ctx);
174+
}
175+
176+
virtual void handleDuplicatedAssignment(const BinaryOperator *Assign,
177+
const BinaryOperator *PrevAssign,
178+
const ValueDecl *VD,
179+
bool IsRelatedToDecl,
180+
ASTContext &Ctx) {
181+
handleUnsafeOperation(Assign, IsRelatedToDecl, Ctx);
182+
}
167183
/* TO_UPSTREAM(BoundsSafety) OFF */
168184

169185
/// 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
@@ -14121,6 +14121,12 @@ def warn_cannot_assign_to_immutable_bounds_attributed_object : Warning<
1412114121
"its type depends on an inout dependent count|"
1412214122
"it's used as dependent count in an inout count-attributed pointer"
1412314123
"}2">, InGroup<UnsafeBufferUsage>, DefaultIgnore;
14124+
def warn_missing_assignments_in_bounds_attributed_group : Warning<
14125+
"bounds-attributed group requires assigning '%0', assignments to '%1' missing">,
14126+
InGroup<UnsafeBufferUsage>, DefaultIgnore;
14127+
def warn_duplicated_assignment_in_bounds_attributed_group : Warning<
14128+
"duplicated assignment to %select{variable|parameter|member}0 %1 in "
14129+
"bounds-attributed group">, InGroup<UnsafeBufferUsage>, DefaultIgnore;
1412414130
#ifndef NDEBUG
1412514131
// Not a user-facing diagnostic. Useful for debugging false negatives in
1412614132
// -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
@@ -5725,14 +5725,73 @@ static bool checkImmutableBoundsAttributedObjects(
57255725
return IsGroupSafe;
57265726
}
57275727

5728+
// Checks if the bounds-attributed group has missing or duplicated assignments.
5729+
// Each assignable decl in the group must be assigned exactly once.
5730+
//
5731+
// For example:
5732+
// void foo(int *__counted_by(a + b) p, int a, int b) {
5733+
// p = ...;
5734+
// p = ...; // duplicated
5735+
// a = ...;
5736+
// // b missing
5737+
// }
5738+
// Note that the group consists of a, b, and p.
5739+
//
5740+
// The function returns true iff each assignable decl in the group is assigned
5741+
// exactly once.
5742+
static bool checkMissingAndDuplicatedAssignments(
5743+
const BoundsAttributedAssignmentGroup &Group,
5744+
UnsafeBufferUsageHandler &Handler, ASTContext &Ctx) {
5745+
llvm::SmallDenseMap<const ValueDecl *, const BinaryOperator *, 4>
5746+
AssignedDecls;
5747+
5748+
DependentDeclSetTy RequiredDecls = Group.DeclClosure;
5749+
for (const ValueDecl *VD : Group.DeclClosure) {
5750+
if (VD->isDependentCountWithoutDeref() &&
5751+
VD->isDependentCountThatIsUsedInInoutPointer()) {
5752+
// This value is immutable, so it's not required to be assigned.
5753+
RequiredDecls.erase(VD);
5754+
}
5755+
}
5756+
5757+
DependentDeclSetTy MissingDecls = RequiredDecls;
5758+
5759+
bool IsGroupSafe = true;
5760+
5761+
for (size_t I = 0, N = Group.Assignments.size(); I < N; ++I) {
5762+
const BinaryOperator *Assign = Group.Assignments[I];
5763+
const ValueDecl *VD = Group.AssignedObjects[I].Decl;
5764+
5765+
const auto [It, Inserted] = AssignedDecls.insert({VD, Assign});
5766+
if (Inserted)
5767+
MissingDecls.erase(VD);
5768+
else {
5769+
const BinaryOperator *PrevAssign = It->second;
5770+
Handler.handleDuplicatedAssignment(Assign, PrevAssign, VD,
5771+
/*IsRelatedToDecl=*/false, Ctx);
5772+
IsGroupSafe = false;
5773+
}
5774+
}
5775+
5776+
if (!MissingDecls.empty()) {
5777+
const Expr *LastAssign = Group.Assignments.back();
5778+
Handler.handleMissingAssignments(LastAssign, RequiredDecls, MissingDecls,
5779+
/*IsRelatedToDecl=*/false, Ctx);
5780+
IsGroupSafe = false;
5781+
}
5782+
5783+
return IsGroupSafe;
5784+
}
5785+
57285786
// Checks if the bounds-attributed group is safe. This function returns false
57295787
// iff the assignment group is unsafe and diagnostics have been emitted.
57305788
static bool
57315789
checkBoundsAttributedGroup(const BoundsAttributedAssignmentGroup &Group,
57325790
UnsafeBufferUsageHandler &Handler, ASTContext &Ctx) {
57335791
if (!checkImmutableBoundsAttributedObjects(Group, Handler, Ctx))
57345792
return false;
5735-
5793+
if (!checkMissingAndDuplicatedAssignments(Group, Handler, Ctx))
5794+
return false;
57365795
// TODO: Add more checks.
57375796
return true;
57385797
}

clang/lib/Sema/AnalysisBasedWarnings.cpp

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

26382680
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)