Skip to content

Commit 5423556

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 (cherry picked from commit c86fc87) Conflicts: clang/test/SemaCXX/warn-unsafe-buffer-usage-libc-functions-interop-annotated.cpp
1 parent 872b2ca commit 5423556

File tree

5 files changed

+94
-6
lines changed

5 files changed

+94
-6
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14094,6 +14094,11 @@ def note_unsafe_count_attributed_pointer_argument : Note<
1409414094
"consider using %select{|a safe container and passing '.data()' to the "
1409514095
"parameter%select{| '%3'}2 and '.size()' to its dependent parameter '%4' or }0"
1409614096
"'std::span' and passing '.first(...).data()' to the parameter%select{| '%3'}2">;
14097+
def note_unsafe_count_attributed_pointer_argument_null_to_nonnull : Note<
14098+
"passing '%select{__counted_by_or_null()|__sized_by_or_null()}0' to "
14099+
"'%select{__counted_by()|__sized_by()}1' while "
14100+
"'%select{__counted_by_or_null()|__sized_by_or_null()}0' can be null with an "
14101+
"arbirary %select{count|size}0">;
1409714102
def warn_unsafe_single_pointer_argument : Warning<
1409814103
"unsafe assignment to function parameter of __single pointer type">,
1409914104
InGroup<UnsafeBufferUsage>, DefaultIgnore;

clang/lib/Analysis/UnsafeBufferUsage.cpp

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

954954
// Checks if the argument passed to count-attributed pointer is one of the
955955
// following forms:
956-
// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`.
956+
// 0. `NULL/nullptr`, if the argument to dependent count/size is `0`, or
957+
// anything if the type of the pointer is
958+
// __counted_by_or_null()/__sized_by_or_null().
957959
// 1. `&var`, if `var` is a variable identifier and (1.a.) the dependent count
958960
// is `1`; or (1.b.) the counted_by expression is constant `1`
959961
// 2. `&var`, if `var` is a variable identifier and the dependent size is
@@ -1016,6 +1018,8 @@ static bool isCountAttributedPointerArgumentSafeImpl(
10161018

10171019
// check form 0:
10181020
if (PtrArgNoImp->getType()->isNullPtrType()) {
1021+
if (isOrNull)
1022+
return true;
10191023
if (CountArg)
10201024
return hasIntegeralConstant(CountArg, 0, Context);
10211025
// When there is no argument representing the count/size, it is safe iff
@@ -1090,7 +1094,7 @@ static bool isCountAttributedPointerArgumentSafeImpl(
10901094
bool areBoundsAttributesCompatible =
10911095
(ArgCAT->isCountInBytes() == isSizedBy || (ArgInBytes && ParamInBytes));
10921096

1093-
if (ArgCAT->isOrNull() == isOrNull && areBoundsAttributesCompatible)
1097+
if ((isOrNull || !ArgCAT->isOrNull()) && areBoundsAttributesCompatible)
10941098
ActualCount = ArgCAT->getCountExpr();
10951099
// In case `PtrArgNoImp` is a function call expression of counted_by type
10961100
// (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
@@ -2579,6 +2579,15 @@ class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
25792579
SourceLocation DiagLoc =
25802580
Arg->isDefaultArgument() ? Call->getRParenLoc() : Arg->getBeginLoc();
25812581
S.Diag(DiagLoc, diag::warn_unsafe_count_attributed_pointer_argument);
2582+
2583+
if (const auto *ArgCATy = Arg->getType()->getAs<CountAttributedType>();
2584+
ArgCATy && ArgCATy->isOrNull() && !CATy->isOrNull()) {
2585+
S.Diag(
2586+
DiagLoc,
2587+
diag::note_unsafe_count_attributed_pointer_argument_null_to_nonnull)
2588+
<< ArgCATy->isCountInBytes() << CATy->isCountInBytes();
2589+
}
2590+
25822591
S.Diag(PVD->getBeginLoc(),
25832592
diag::note_unsafe_count_attributed_pointer_argument)
25842593
<< 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+
}

0 commit comments

Comments
 (0)