Skip to content

Commit c86fc87

Browse files
[-Wunsafe-buffer-usage] Relax passing to __counted_by_or_null()
Relax restrictions when passing to __counted_by_or_null()/__sized_by_or_null() parameters: - Allow the dependent count var to be anything. - Allow passing from non-null variant to *_or_null(), but do warn for passing *_or_null() to non-null variant. rdar://156006635
1 parent ccce5dc commit c86fc87

6 files changed

+109
-9
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14228,6 +14228,11 @@ def note_unsafe_count_attributed_pointer_argument : Note<
1422814228
"consider using %select{|a safe container and passing '.data()' to the "
1422914229
"parameter%select{| '%3'}2 and '.size()' to its dependent parameter '%4' or }0"
1423014230
"'std::span' and passing '.first(...).data()' to the parameter%select{| '%3'}2">;
14231+
def note_unsafe_count_attributed_pointer_argument_null_to_nonnull : Note<
14232+
"passing '%select{__counted_by_or_null()|__sized_by_or_null()}0' to "
14233+
"'%select{__counted_by()|__sized_by()}1' while "
14234+
"'%select{__counted_by_or_null()|__sized_by_or_null()}0' can be null with an "
14235+
"arbirary %select{count|size}0">;
1423114236
def warn_unsafe_single_pointer_argument : Warning<
1423214237
"unsafe assignment to function parameter of __single pointer type">,
1423314238
InGroup<UnsafeBufferUsage>, DefaultIgnore;

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -958,7 +958,9 @@ static const DeclRefExpr *tryGetAddressofDRE(const Expr *E) {
958958

959959
// Checks if the argument passed to count-attributed pointer is one of the
960960
// following forms:
961-
// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`.
961+
// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`, or
962+
// anything if the type of the pointer is
963+
// __counted_by_or_null()/__sized_by_or_null().
962964
// 1. `&var`, if `var` is a variable identifier and (1.a.) the dependent count
963965
// is `1`; or (1.b.) the counted_by expression is constant `1`
964966
// 2. `&var`, if `var` is a variable identifier and the dependent size is
@@ -1021,6 +1023,8 @@ static bool isCountAttributedPointerArgumentSafeImpl(
10211023

10221024
// check form 0:
10231025
if (PtrArgNoImp->getType()->isNullPtrType()) {
1026+
if (isOrNull)
1027+
return true;
10241028
if (CountArg)
10251029
return hasIntegeralConstant(CountArg, 0, Context);
10261030
// When there is no argument representing the count/size, it is safe iff
@@ -1095,7 +1099,7 @@ static bool isCountAttributedPointerArgumentSafeImpl(
10951099
bool areBoundsAttributesCompatible =
10961100
(ArgCAT->isCountInBytes() == isSizedBy || (ArgInBytes && ParamInBytes));
10971101

1098-
if (ArgCAT->isOrNull() == isOrNull && areBoundsAttributesCompatible)
1102+
if ((isOrNull || !ArgCAT->isOrNull()) && areBoundsAttributesCompatible)
10991103
ActualCount = ArgCAT->getCountExpr();
11001104
// In case `PtrArgNoImp` is a function call expression of counted_by type
11011105
// (i.e., return type is a CAT), create a map for the call:

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,6 +2590,15 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
25902590
SourceLocation DiagLoc =
25912591
Arg->isDefaultArgument() ? Call->getRParenLoc() : Arg->getBeginLoc();
25922592
S.Diag(DiagLoc, diag::warn_unsafe_count_attributed_pointer_argument);
2593+
2594+
if (const auto *ArgCATy = Arg->getType()->getAs<CountAttributedType>();
2595+
ArgCATy && ArgCATy->isOrNull() && !CATy->isOrNull()) {
2596+
S.Diag(
2597+
DiagLoc,
2598+
diag::note_unsafe_count_attributed_pointer_argument_null_to_nonnull)
2599+
<< ArgCATy->isCountInBytes() << CATy->isCountInBytes();
2600+
}
2601+
25932602
S.Diag(PVD->getBeginLoc(),
25942603
diag::note_unsafe_count_attributed_pointer_argument)
25952604
<< IsSimpleCount << QualType(CATy, 0) << !PtrParamName.empty()

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

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void cb_cchar(const char *__counted_by(len) s, size_t len);
6969
// expected-note@+1 3{{consider using 'std::span' and passing '.first(...).data()' to the parameter 's'}}
7070
void cb_cchar_42(const char *__counted_by(42) s);
7171

72-
// expected-note@+1 31{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
72+
// expected-note@+1 +{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
7373
void cb_int(int *__counted_by(count) p, size_t count);
7474

7575
// expected-note@+1 34{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
@@ -81,6 +81,9 @@ void cb_cint_42(const int *__counted_by(42) p);
8181
// expected-note@+1 6{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
8282
void cb_cint_multi(const int *__counted_by((a + b) * (c - d)) p, int a, int b, int c, int d);
8383

84+
// expected-note@+1 3{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
85+
void sb_void(void *__sized_by(size) p, size_t size);
86+
8487
// expected-note@+1 13{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
8588
void sb_cvoid(const void *__sized_by(size) p, size_t size);
8689

@@ -98,6 +101,12 @@ void sb_cint_42(const int *__sized_by(42) p);
98101

99102
void cb_cint_array(const int (* __counted_by(size) p)[10], size_t size);
100103

104+
// expected-note@+1 +{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
105+
void cbn_int(int *__counted_by_or_null(count) p, size_t count);
106+
107+
// expected-note@+1 +{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
108+
void sbn_void(void *__sized_by_or_null(size) p, size_t size);
109+
101110
} // extern "C"
102111

103112
// Check allowed classes and method.
@@ -389,9 +398,22 @@ void from_cb_int_multi(int *__counted_by((a + b) * (c - d)) p, int a, int b, int
389398
cb_int(p, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
390399
}
391400

392-
void nullptr_as_arg(void * __sized_by(size) p, unsigned size) { //expected-note{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
393-
nullptr_as_arg(nullptr, 0);
394-
nullptr_as_arg(nullptr, size); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
401+
void nullptr_as_arg(size_t n) {
402+
cb_int(nullptr, 0);
403+
cb_int(nullptr, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
404+
cb_int(nullptr, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
405+
406+
sb_void(nullptr, 0);
407+
sb_void(nullptr, 42); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
408+
sb_void(nullptr, n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
409+
410+
cbn_int(nullptr, 0);
411+
cbn_int(nullptr, 42);
412+
cbn_int(nullptr, n);
413+
414+
sbn_void(nullptr, 0);
415+
sbn_void(nullptr, 42);
416+
sbn_void(nullptr, n);
395417
}
396418

397419
void single_variable() {
@@ -708,6 +730,42 @@ static void previous_infinite_loop3(int * __counted_by(n + n * m) p, size_t n,
708730
previous_infinite_loop3(p, n, q, r, m, o + 1); // expected-warning 2{{unsafe assignment to function parameter of count-attributed type}}
709731
}
710732

733+
// Check nullable variants.
734+
735+
void nullable(std::span<int> sp, size_t n) {
736+
cbn_int(sp.data(), sp.size());
737+
cbn_int(sp.data(), sp.size_bytes()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
738+
cbn_int(sp.first(42).data(), 42);
739+
cbn_int(sp.first(n).data(), n);
740+
cbn_int(sp.first(42).data(), n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
741+
742+
sbn_void(sp.data(), sp.size_bytes());
743+
sbn_void(sp.data(), sp.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
744+
sbn_void(sp.first(42).data(), n); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
745+
}
746+
747+
void nonnull_tofrom_nullable(size_t n,
748+
int *__counted_by(n) cb,
749+
int *__counted_by_or_null(n) cbn,
750+
void *__sized_by(n) sb,
751+
void *__sized_by_or_null(n) sbn) {
752+
cb_int(cb, n);
753+
// expected-note@+2{{passing '__counted_by_or_null()' to '__counted_by()' while '__counted_by_or_null()' can be null with an arbirary count}}
754+
// expected-warning@+1{{unsafe assignment to function parameter of count-attributed type}}
755+
cb_int(cbn, n);
756+
757+
cbn_int(cb, n);
758+
cbn_int(cbn, n);
759+
760+
sb_void(sb, n);
761+
// expected-note@+2{{passing '__sized_by_or_null()' to '__sized_by()' while '__sized_by_or_null()' can be null with an arbirary size}}
762+
// expected-warning@+1{{unsafe assignment to function parameter of count-attributed type}}
763+
sb_void(sbn, n);
764+
765+
sbn_void(sb, n);
766+
sbn_void(sbn, n);
767+
}
768+
711769
// Check default args.
712770

713771
void cb_def_arg_null_0(int *__counted_by(count) p = nullptr, size_t count = 0);

clang/test/SemaCXX/warn-unsafe-buffer-usage-in-container-span-construct-count-attributed-pointer.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,15 @@ namespace test_fam {
176176
}
177177

178178
} // namespace test_fam
179+
180+
void test_nullable(int *__counted_by_or_null(count) cbn_p, size_t count,
181+
char *__sized_by_or_null(size) sbn_p, size_t size) {
182+
// __counted_by_or_null(count) pointer can be null with an arbitrary count.
183+
// We rely here on the dynamic check performed by std::span constructor, and
184+
// allow those patterns.
185+
std::span<int>{cbn_p, count};
186+
std::span<char>{sbn_p, size};
187+
188+
std::span<int>{cbn_p, 42}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}}
189+
std::span<char>{sbn_p, 42}; // expected-warning{{the two-parameter std::span construction is unsafe as it can introduce mismatch between buffer size and the bound information}}
190+
}

clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-interop-annotated.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ namespace std {
3939
// expected-note@+1{{consider using a safe container and passing '.data()' to the parameter 'src' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'src'}}
4040
void *memcpy(void * __sized_by(size) dst, const void * __sized_by(size) src, size_t size);
4141
size_t strlen( const char* __null_terminated str );
42-
// expected-note@+1{{consider using a safe container and passing '.data()' to the parameter 'buffer' and '.size()' to its dependent parameter 'buf_size' or 'std::span' and passing '.first(...).data()' to the parameter 'buffer'}}
43-
int snprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, ... );
4442
// expected-note@+1 2{{consider using a safe container and passing '.data()' to the parameter 'buffer' and '.size()' to its dependent parameter 'buf_size' or 'std::span' and passing '.first(...).data()' to the parameter 'buffer'}}
43+
int snprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, ... );
44+
// expected-note@+1 +{{consider using a safe container and passing '.data()' to the parameter 'buffer' and '.size()' to its dependent parameter 'buf_size' or 'std::span' and passing '.first(...).data()' to the parameter 'buffer'}}
4545
int snwprintf( wchar_t* __counted_by(buf_size) buffer, size_t buf_size, const wchar_t* format, ... );
4646
int vsnprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, va_list va_args);
4747

@@ -55,7 +55,8 @@ void test(char * p, char * q, const char * str,
5555
const char * __null_terminated safe_str,
5656
char * __counted_by(n) safe_p,
5757
size_t n,
58-
char * __counted_by(10) safe_ten) {
58+
char * __counted_by(10) safe_ten,
59+
char * __counted_by_or_null(n) null_p) {
5960
memcpy(p, q, 10); // expected-warning2{{unsafe assignment to function parameter of count-attributed type}}
6061
snprintf(p, 10, "%s", "hlo"); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
6162

@@ -73,13 +74,18 @@ void test(char * p, char * q, const char * str,
7374
vsnprintf(safe_p, n, "%s", vlist); // expected-warning{{function 'vsnprintf' is unsafe}} expected-note{{'va_list' is unsafe}}
7475
sprintf(safe_ten, "%s", safe_str); // expected-warning{{function 'sprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
7576

77+
// expected-warning@+2{{unsafe assignment to function parameter of count-attributed type}}
78+
// expected-note@+1{{passing '__counted_by_or_null()' to '__counted_by()' while '__counted_by_or_null()' can be null with an arbirary count}}
79+
snprintf(null_p, n, "%s", "hlo");
7680
}
7781

7882
void test_wchar(wchar_t * p, wchar_t * q, const wchar_t * wstr,
7983
const wchar_t * __null_terminated safe_wstr,
8084
wchar_t * __null_terminated nt_wstr,
8185
wchar_t * __counted_by(n) safe_p,
8286
wchar_t * __sized_by(n) sizedby_p,
87+
wchar_t * __counted_by_or_null(n) cbn,
88+
wchar_t * __sized_by_or_null(n) sbn,
8389
size_t n) {
8490
std::wstring cxx_wstr;
8591
std::span<wchar_t> cxx_wspan;
@@ -90,5 +96,11 @@ void test_wchar(wchar_t * p, wchar_t * q, const wchar_t * wstr,
9096
snwprintf(p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
9197
snwprintf(sizedby_p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
9298
snwprintf(safe_p, n, L"%ls", wstr); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
99+
// expected-warning@+2{{unsafe assignment to function parameter of count-attributed type}}
100+
// expected-note@+1{{passing '__sized_by_or_null()' to '__counted_by()' while '__sized_by_or_null()' can be null with an arbirary size}}
101+
snwprintf(sbn, n, L"%ls", safe_wstr);
102+
// expected-warning@+2{{unsafe assignment to function parameter of count-attributed type}}
103+
// expected-note@+1{{passing '__counted_by_or_null()' to '__counted_by()' while '__counted_by_or_null()' can be null with an arbirary count}}
104+
snwprintf(cbn, n, L"%ls", wstr);
93105
}
94106

0 commit comments

Comments
 (0)