Skip to content

Commit 93c0cd0

Browse files
[-Wunsafe-buffer-usage] Check assignments to __single pointer
This introduces a new warning gadget that matches assignments to __single pointers. To verify whether the assignment is safe or not, we use the same logic as for __single pointer arguments. rdar://128158123 (cherry picked from commit 46ed789) Conflicts: clang/include/clang/Basic/DiagnosticSemaKinds.td
1 parent d402ea5 commit 93c0cd0

File tree

6 files changed

+234
-0
lines changed

6 files changed

+234
-0
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ class UnsafeBufferUsageHandler {
143143
handleUnsafeOperation(Arg, IsRelatedToDecl, Ctx);
144144
}
145145

146+
/// Invoked when an unsafe assignment to __single pointer is found.
147+
virtual void handleUnsafeSinglePointerAssignment(const BinaryOperator *Assign,
148+
bool IsRelatedToDecl,
149+
ASTContext &Ctx) {
150+
handleUnsafeOperation(Assign, IsRelatedToDecl, Ctx);
151+
}
152+
146153
virtual void handleTooComplexCountAttributedAssign(const Expr *E,
147154
const ValueDecl *VD,
148155
bool IsRelatedToDecl,

clang/include/clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ WARNING_GADGET(DataInvocation)
4747
// TO_UPSTREAM(BoundsSafety) ON
4848
WARNING_BOUNDS_SAFETY_GADGET(CountAttributedPointerArgument)
4949
WARNING_BOUNDS_SAFETY_GADGET(SinglePointerArgument)
50+
WARNING_BOUNDS_SAFETY_GADGET(SinglePointerAssignment)
5051
// TO_UPSTREAM(BoundsSafety) OFF
5152
WARNING_OPTIONAL_GADGET(UnsafeLibcFunctionCall)
5253
WARNING_OPTIONAL_GADGET(SpanTwoParamConstructor) // Uses of `std::span(arg0, arg1)`

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14114,6 +14114,9 @@ def note_unsafe_count_attributed_pointer_argument_null_to_nonnull : Note<
1411414114
def warn_unsafe_single_pointer_argument : Warning<
1411514115
"unsafe assignment to function parameter of __single pointer type">,
1411614116
InGroup<UnsafeBufferUsage>, DefaultIgnore;
14117+
def warn_unsafe_single_pointer_assignment : Warning<
14118+
"unsafe assignment to __single pointer type">,
14119+
InGroup<UnsafeBufferUsage>, DefaultIgnore;
1411714120
def warn_assign_to_count_attributed_must_be_simple_stmt : Warning<
1411814121
"assignment to %select{count-attributed pointer|dependent count}0 '%1' "
1411914122
"must be a simple statement '%1 = ...'">,

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,6 +3533,65 @@ class SinglePointerArgumentGadget : public WarningGadget {
35333533
}
35343534
};
35353535

3536+
// Represents an assignment to a __single pointer.
3537+
class SinglePointerAssignmentGadget : public WarningGadget {
3538+
private:
3539+
static constexpr const char *const AssignTag = "SinglePointerAssignment_Assign";
3540+
const BinaryOperator *Assign;
3541+
3542+
public:
3543+
explicit SinglePointerAssignmentGadget(const MatchResult &Result)
3544+
: WarningGadget(Kind::SinglePointerAssignment),
3545+
Assign(Result.getNodeAs<BinaryOperator>(AssignTag)) {
3546+
assert(Assign != nullptr && "Expecting a non-null matching result");
3547+
}
3548+
3549+
static bool classof(const Gadget *G) {
3550+
return G->getKind() == Kind::SinglePointerAssignment;
3551+
}
3552+
3553+
static bool matches(const Stmt *S, ASTContext &Ctx,
3554+
llvm::SmallVectorImpl<MatchResult> &Results) {
3555+
bool Found = false;
3556+
findStmtsInUnspecifiedUntypedContext(
3557+
S, [&Results, &Ctx, &Found](const Stmt *S) {
3558+
const auto *E = dyn_cast<Expr>(S);
3559+
if (!E)
3560+
return;
3561+
const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
3562+
if (!BO || BO->getOpcode() != BO_Assign)
3563+
return;
3564+
QualType LHSTy = BO->getLHS()->getType();
3565+
if (!LHSTy->isSinglePointerType())
3566+
return;
3567+
if (isSinglePointerArgumentSafe(Ctx, LHSTy, BO->getRHS()))
3568+
return;
3569+
Results.emplace_back(AssignTag, DynTypedNode::create(*BO));
3570+
Found = true;
3571+
});
3572+
return Found;
3573+
}
3574+
3575+
void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
3576+
bool IsRelatedToDecl,
3577+
ASTContext &Ctx) const override {
3578+
Handler.handleUnsafeSinglePointerAssignment(Assign, IsRelatedToDecl, Ctx);
3579+
}
3580+
3581+
SourceLocation getSourceLoc() const override {
3582+
return Assign->getOperatorLoc();
3583+
}
3584+
3585+
virtual DeclUseList getClaimedVarUseSites() const override {
3586+
if (const auto *DRE = dyn_cast<DeclRefExpr>(Assign->getLHS())) {
3587+
return {DRE};
3588+
}
3589+
return {};
3590+
}
3591+
3592+
SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
3593+
};
3594+
35363595
/// Scan the function and return a list of gadgets found with provided kits.
35373596
class WarningGadgetMatcher : public FastMatcher {
35383597

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2601,6 +2601,13 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
26012601
S.Diag(Arg->getBeginLoc(), diag::warn_unsafe_single_pointer_argument);
26022602
}
26032603

2604+
void handleUnsafeSinglePointerAssignment(
2605+
const BinaryOperator *Assign, [[maybe_unused]] bool IsRelatedToDecl,
2606+
[[maybe_unused]] ASTContext &Ctx) override {
2607+
S.Diag(Assign->getOperatorLoc(),
2608+
diag::warn_unsafe_single_pointer_assignment);
2609+
}
2610+
26042611
void handleTooComplexCountAttributedAssign(
26052612
const Expr *E, const ValueDecl *VD, [[maybe_unused]] bool IsRelatedToDecl,
26062613
[[maybe_unused]] ASTContext &Ctx) override {
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -Wno-all -Wunsafe-buffer-usage -fexperimental-bounds-safety-attributes -verify %s
2+
3+
#include <ptrcheck.h>
4+
#include <stddef.h>
5+
6+
namespace std {
7+
8+
template <typename T, size_t N>
9+
struct array {
10+
T &operator[](size_t n) noexcept;
11+
};
12+
13+
template <typename CharT>
14+
struct basic_string {
15+
CharT &operator[](size_t n) noexcept;
16+
};
17+
18+
typedef basic_string<char> string;
19+
20+
template <typename CharT>
21+
struct basic_string_view {
22+
const CharT &operator[](size_t n) const noexcept;
23+
};
24+
25+
typedef basic_string_view<char> string_view;
26+
27+
template <typename T>
28+
struct span {
29+
T *data() const noexcept;
30+
span<T> first(size_t count) const noexcept;
31+
span<T> last(size_t count) const noexcept;
32+
span<T> subspan(size_t offset, size_t count) const noexcept;
33+
T &operator[](size_t n) noexcept;
34+
};
35+
36+
template <typename T>
37+
struct vector {
38+
T &operator[](size_t n) noexcept;
39+
};
40+
41+
} // namespace std
42+
43+
template <typename T>
44+
struct my_vec {
45+
T &operator[](size_t n) noexcept;
46+
};
47+
48+
// Check assignment to `void *__single`.
49+
50+
void single_void(void *__single p_void, void *pv, std::span<int> sp, my_vec<int> &mv) {
51+
char array[42] = {};
52+
53+
p_void = pv;
54+
55+
p_void = sp.data();
56+
p_void = sp.first(1).data();
57+
p_void = sp.first(42).data();
58+
p_void = &sp[42];
59+
60+
p_void = &mv[0];
61+
62+
p_void = array;
63+
}
64+
65+
// Check `nullptr`.
66+
67+
void null(char *__single p_char,
68+
const char *__single p_cchar,
69+
int *__single p_int,
70+
void *__single p_void) {
71+
p_char = nullptr;
72+
p_cchar = nullptr;
73+
p_int = nullptr;
74+
p_void = nullptr;
75+
}
76+
77+
// Check `&var` pattern.
78+
79+
void addr_of_var(char *__single p_char,
80+
const char *__single p_cchar,
81+
int *__single p_int,
82+
void *__single p_void) {
83+
char c = 0;
84+
p_char = &c;
85+
p_cchar = &c;
86+
p_void = &c;
87+
88+
int i = 0;
89+
p_int = &i;
90+
p_void = &i;
91+
}
92+
93+
// Check allowed classes in `&C[index]` pattern.
94+
95+
void allowed_class(char *__single p_char,
96+
const char *__single p_cchar,
97+
int *__single p_int,
98+
void *__single p_void,
99+
std::array<int, 42> &a,
100+
std::string &s,
101+
std::string_view sv,
102+
std::span<int> sp,
103+
std::vector<int> &v) {
104+
p_int = &a[0];
105+
p_void = &a[0];
106+
107+
p_char = &s[0];
108+
p_cchar = &s[0];
109+
p_void = &s[0];
110+
111+
p_cchar = &sv[0];
112+
113+
p_int = &sp[0];
114+
p_void = &sp[0];
115+
116+
p_int = &v[0];
117+
p_void = &v[0];
118+
}
119+
120+
void not_allowed_class(int *__single p_int, my_vec<int> &mv) {
121+
p_int = &mv[0]; // expected-warning{{unsafe assignment to __single pointer type}}
122+
}
123+
124+
// Check if index doesn't matter in `&C[index]` pattern.
125+
126+
void index_does_not_matter(int *__single p_int, std::span<int> sp, size_t index) {
127+
p_int = &sp[0];
128+
p_int = &sp[1];
129+
p_int = &sp[index];
130+
p_int = &sp[42 - index];
131+
}
132+
133+
// Check span's subview pattern.
134+
135+
void span_subview(int *__single p_int, std::span<int> sp, int n) {
136+
p_int = sp.first(1).data();
137+
p_int = sp.first(0).data(); // expected-warning{{unsafe assignment to __single pointer type}}
138+
p_int = sp.first(n).data(); // expected-warning{{unsafe assignment to __single pointer type}}
139+
140+
p_int = sp.last(1).data();
141+
p_int = sp.last(0).data(); // expected-warning{{unsafe assignment to __single pointer type}}
142+
p_int = sp.last(n).data(); // expected-warning{{unsafe assignment to __single pointer type}}
143+
144+
p_int = sp.subspan(0, 1).data();
145+
p_int = sp.subspan(42, 1).data();
146+
p_int = sp.subspan(n, 1).data();
147+
p_int = sp.subspan(0, 0).data(); // expected-warning{{unsafe assignment to __single pointer type}}
148+
p_int = sp.subspan(0, n).data(); // expected-warning{{unsafe assignment to __single pointer type}}
149+
}
150+
151+
// Check common unsafe patterns.
152+
153+
void unsafe(int *__single p_int, std::span<int> sp, int *p) {
154+
p_int = sp.data(); // expected-warning{{unsafe assignment to __single pointer type}}
155+
156+
p_int = p; // expected-warning{{unsafe assignment to __single pointer type}}
157+
}

0 commit comments

Comments
 (0)