Skip to content

Commit bb71067

Browse files
committed
[-Wunsafe-buffer-usage] Ignore constant safe indices in array subscripts (llvm#80504)
[-Wunsafe-buffer-usage] Ignore safe array subscripts Don't emit warnings for array subscripts on constant size arrays where the index is constant and within bounds. Example: int arr[10]; arr[5] = 0; //safe, no warning This patch recognizes only array indices that are integer literals - it doesn't understand more complex expressions (arithmetic on constants, etc.). -Warray-bounds implemented in Sema::CheckArrayAccess() already solves a similar (opposite) problem, handles complex expressions and is battle-tested. Adding -Wunsafe-buffer-usage diagnostics to Sema is a non-starter as we need to emit both the warnings and fixits and the performance impact of the fixit machine is unacceptable for Sema. CheckArrayAccess() as is doesn't distinguish between "safe" and "unknown" array accesses. It also mixes the analysis that decides if an index is out of bounds with crafting the diagnostics. A refactor of CheckArrayAccess() might serve both the original purpose and help us avoid false-positive with -Wunsafe-buffer-usage on constant size arrrays. (cherry picked from commit 9a1e637)
1 parent 0df1cf0 commit bb71067

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)