Skip to content

Commit 5fe208d

Browse files
authored
Merge pull request #8196 from jkorous-apple/cxx-safe-buffers/integrate/ignore-constant-safe-subscripts
[-Wunsafe-buffer-usage] Ignore constant safe indices in array subscri…
2 parents 0df1cf0 + bb71067 commit 5fe208d

5 files changed

+90
-40
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,39 @@ AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
407407
}
408408
return false;
409409
}
410+
411+
AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
412+
// FIXME: Proper solution:
413+
// - refactor Sema::CheckArrayAccess
414+
// - split safe/OOB/unknown decision logic from diagnostics emitting code
415+
// - e. g. "Try harder to find a NamedDecl to point at in the note."
416+
// already duplicated
417+
// - call both from Sema and from here
418+
419+
const auto *BaseDRE =
420+
dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts());
421+
if (!BaseDRE)
422+
return false;
423+
if (!BaseDRE->getDecl())
424+
return false;
425+
const auto *CATy = Finder->getASTContext().getAsConstantArrayType(
426+
BaseDRE->getDecl()->getType());
427+
if (!CATy)
428+
return false;
429+
const APInt ArrSize = CATy->getSize();
430+
431+
if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
432+
const APInt ArrIdx = IdxLit->getValue();
433+
// FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
434+
// bug
435+
if (ArrIdx.isNonNegative() &&
436+
ArrIdx.getLimitedValue() < ArrSize.getLimitedValue())
437+
return true;
438+
}
439+
440+
return false;
441+
}
442+
410443
} // namespace clang::ast_matchers
411444

412445
namespace {
@@ -599,16 +632,16 @@ class ArraySubscriptGadget : public WarningGadget {
599632
}
600633

601634
static Matcher matcher() {
602-
// FIXME: What if the index is integer literal 0? Should this be
603-
// a safe gadget in this case?
604635
// clang-format off
605636
return stmt(arraySubscriptExpr(
606637
hasBase(ignoringParenImpCasts(
607638
anyOf(hasPointerType(), hasArrayType()))),
608-
unless(hasIndex(
609-
anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
610-
)))
611-
.bind(ArraySubscrTag));
639+
unless(anyOf(
640+
isSafeArraySubscript(),
641+
hasIndex(
642+
anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
643+
)
644+
))).bind(ArraySubscrTag));
612645
// clang-format on
613646
}
614647

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage \
1+
// RUN: %clang_cc1 -std=c++20 -Wno-everything -Wunsafe-buffer-usage \
22
// RUN: -fsafe-buffer-usage-suggestions \
33
// RUN: -verify %s
44

@@ -22,3 +22,19 @@ struct Foo {
2222
void foo2(Foo& f, unsigned idx) {
2323
f.member_buffer[idx] = 0; // expected-warning{{unsafe buffer access}}
2424
}
25+
26+
void constant_idx_safe(unsigned idx) {
27+
int buffer[10];
28+
buffer[9] = 0;
29+
}
30+
31+
void constant_idx_safe0(unsigned idx) {
32+
int buffer[10];
33+
buffer[0] = 0;
34+
}
35+
36+
void constant_idx_unsafe(unsigned idx) {
37+
int buffer[10]; // expected-warning{{'buffer' is an unsafe buffer that does not perform bounds checks}}
38+
// expected-note@-1{{change type of 'buffer' to 'std::array' to label it for hardening}}
39+
buffer[10] = 0; // expected-note{{used in buffer access here}}
40+
}

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-access.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,11 @@ void unsafe_method_invocation_single_param() {
8383

8484
}
8585

86-
void unsafe_method_invocation_single_param_array() {
86+
void unsafe_method_invocation_single_param_array(int idx) {
8787
int p[32];
8888
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::array<int, 32> p"
8989

90-
int tmp = p[5];
90+
int tmp = p[idx];
9191
foo(p);
9292
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:8-[[@LINE-1]]:8}:".data()"
9393
}
@@ -126,14 +126,14 @@ void unsafe_method_invocation_double_param() {
126126
m1(q, q, 8);
127127
}
128128

129-
void unsafe_method_invocation_double_param_array() {
129+
void unsafe_method_invocation_double_param_array(int idx) {
130130
int p[14];
131131
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::array<int, 14> p"
132132

133133
int q[40];
134134
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::array<int, 40> q"
135135

136-
q[5] = p[5];
136+
q[idx] = p[idx];
137137

138138
m1(p, p, 10);
139139
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:7-[[@LINE-1]]:7}:".data()"

clang/test/SemaCXX/warn-unsafe-buffer-usage-fixits-pointer-arg-to-func-ptr-call.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ void unsafe_array_func_ptr_call(void (*fn_ptr)(int *param)) {
66
int p[32];
77
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:3-[[@LINE-1]]:12}:"std::array<int, 32> p"
88

9-
p[5] = 10;
9+
int idx;
10+
p[idx] = 10;
1011
fn_ptr(p);
1112
// CHECK-DAG: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:11}:".data()"
1213
}

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

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void testIncrement(char *p) { // expected-warning{{'p' is an unsafe pointer used
3636
void * voidPtrCall(void);
3737
char * charPtrCall(void);
3838

39-
void testArraySubscripts(int *p, int **pp) {
39+
void testArraySubscripts(int idx, int *p, int **pp) {
4040
// expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
4141
// expected-warning@-2{{'pp' is an unsafe pointer used for buffer access}}
4242
foo(p[1], // expected-note{{used in buffer access here}}
@@ -64,13 +64,14 @@ void testArraySubscripts(int *p, int **pp) {
6464
// expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}}
6565
int b[10][10]; // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
6666

67-
foo(a[1], 1[a], // expected-note2{{used in buffer access here}}
68-
b[3][4], // expected-warning{{unsafe buffer access}}
69-
// expected-note@-1{{used in buffer access here}}
70-
4[b][3], // expected-warning{{unsafe buffer access}}
71-
// expected-note@-1{{used in buffer access here}}
72-
4[3[b]]); // expected-warning{{unsafe buffer access}}
73-
// expected-note@-1{{used in buffer access here}}
67+
foo(a[idx], idx[a], // expected-note2{{used in buffer access here}}
68+
b[idx][idx + 1], // expected-warning{{unsafe buffer access}}
69+
// expected-note@-1{{used in buffer access here}}
70+
(idx + 1)[b][idx],// expected-warning{{unsafe buffer access}}
71+
// expected-note@-1{{used in buffer access here}}
72+
(idx + 1)[idx[b]]);
73+
// expected-warning@-1{{unsafe buffer access}}
74+
// expected-note@-2{{used in buffer access here}}
7475

7576
// Not to warn when index is zero
7677
foo(p[0], pp[0][0], 0[0[pp]], 0[pp][0],
@@ -158,9 +159,9 @@ void testLambdaCaptureAndGlobal(int * p) {
158159
// expected-warning@-1{{'p' is an unsafe pointer used for buffer access}}
159160
int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
160161

161-
auto Lam = [p, a]() {
162+
auto Lam = [p, a](int idx) {
162163
return p[1] // expected-note{{used in buffer access here}}
163-
+ a[1] + garray[1] // expected-note2{{used in buffer access here}}
164+
+ a[idx] + garray[idx]// expected-note2{{used in buffer access here}}
164165
+ gp[1]; // expected-note{{used in buffer access here}}
165166

166167
};
@@ -178,31 +179,31 @@ void testLambdaCapture() {
178179
// expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}}
179180
int c[10];
180181

181-
auto Lam1 = [a]() {
182-
return a[1]; // expected-note{{used in buffer access here}}
182+
auto Lam1 = [a](unsigned idx) {
183+
return a[idx]; // expected-note{{used in buffer access here}}
183184
};
184185

185-
auto Lam2 = [x = b[3]]() { // expected-note{{used in buffer access here}}
186+
auto Lam2 = [x = b[c[5]]]() { // expected-note{{used in buffer access here}}
186187
return x;
187188
};
188189

189-
auto Lam = [x = c]() { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
190-
return x[3]; // expected-note{{used in buffer access here}}
190+
auto Lam = [x = c](unsigned idx) { // expected-warning{{'x' is an unsafe pointer used for buffer access}}
191+
return x[idx]; // expected-note{{used in buffer access here}}
191192
};
192193
}
193194

194-
void testLambdaImplicitCapture() {
195+
void testLambdaImplicitCapture(long idx) {
195196
int a[10]; // expected-warning{{'a' is an unsafe buffer that does not perform bounds checks}}
196197
// expected-note@-1{{change type of 'a' to 'std::array' to label it for hardening}}
197198
int b[10]; // expected-warning{{'b' is an unsafe buffer that does not perform bounds checks}}
198199
// expected-note@-1{{change type of 'b' to 'std::array' to label it for hardening}}
199200

200201
auto Lam1 = [=]() {
201-
return a[1]; // expected-note{{used in buffer access here}}
202+
return a[idx]; // expected-note{{used in buffer access here}}
202203
};
203204

204205
auto Lam2 = [&]() {
205-
return b[1]; // expected-note{{used in buffer access here}}
206+
return b[idx]; // expected-note{{used in buffer access here}}
206207
};
207208
}
208209

@@ -344,38 +345,37 @@ int testVariableDecls(int * p) {
344345
return p[1]; // expected-note{{used in buffer access here}}
345346
}
346347

347-
template<typename T> void fArr(T t[]) {
348+
template<typename T> void fArr(T t[], long long idx) {
348349
// expected-warning@-1{{'t' is an unsafe pointer used for buffer access}}
349350
foo(t[1]); // expected-note{{used in buffer access here}}
350351
T ar[8]; // expected-warning{{'ar' is an unsafe buffer that does not perform bounds checks}}
351352
// expected-note@-1{{change type of 'ar' to 'std::array' to label it for hardening}}
352-
foo(ar[5]); // expected-note{{used in buffer access here}}
353+
foo(ar[idx]); // expected-note{{used in buffer access here}}
353354
}
354355

355-
template void fArr<int>(int t[]); // FIXME: expected note {{in instantiation of}}
356+
template void fArr<int>(int t[], long long); // FIXME: expected note {{in instantiation of}}
356357

357358
int testReturn(int t[]) {// expected-note{{change type of 't' to 'std::span' to preserve bounds information}}
358359
// expected-warning@-1{{'t' is an unsafe pointer used for buffer access}}
359360
return t[1]; // expected-note{{used in buffer access here}}
360361
}
361362

362-
int testArrayAccesses(int n) {
363+
int testArrayAccesses(int n, int idx) {
363364
// auto deduced array type
364365
int cArr[2][3] = {{1, 2, 3}, {4, 5, 6}};
365366
// expected-warning@-1{{'cArr' is an unsafe buffer that does not perform bounds checks}}
366367
int d = cArr[0][0];
367368
foo(cArr[0][0]);
368-
foo(cArr[1][2]); // expected-note{{used in buffer access here}}
369-
// expected-warning@-1{{unsafe buffer access}}
370-
auto cPtr = cArr[1][2]; // expected-note{{used in buffer access here}}
371-
// expected-warning@-1{{unsafe buffer access}}
369+
foo(cArr[idx][idx + 1]); // expected-note{{used in buffer access here}}
370+
// expected-warning@-1{{unsafe buffer access}}
371+
auto cPtr = cArr[idx][idx * 2]; // expected-note{{used in buffer access here}}
372+
// expected-warning@-1{{unsafe buffer access}}
372373
foo(cPtr);
373374

374375
// Typdefs
375376
typedef int A[3];
376377
const A tArr = {4, 5, 6};
377-
// expected-warning@-1{{'tArr' is an unsafe buffer that does not perform bounds checks}}
378-
foo(tArr[0], tArr[1]); // expected-note{{used in buffer access here}}
378+
foo(tArr[0], tArr[1]);
379379
return cArr[0][1]; // expected-warning{{unsafe buffer access}}
380380
}
381381

0 commit comments

Comments
 (0)