Skip to content

Commit cb2ceda

Browse files
committed
[UnsafeBufferUsage] Merge commit '0abf4975bbf1' from llvm.org/main into next
Conflicts: clang/lib/Analysis/UnsafeBufferUsage.cpp The upstream commit no longer matches libc functions in namespaces, unless the namespace is `::std`. This splits part of the downstream test case `warn-unsafe-buffer-usage-libc-functions-interop.cpp` to `warn-unsafe-buffer-usage-libc-functions-interop-annotated.cpp`, to avoid having to use namespaces to disambiguate the overlapping function definitions. Also changes some of the code surrounding the merge conflict to align closer to upstream, (with no change in functionality). rdar://157725434
2 parents 39e1ca9 + 0abf497 commit cb2ceda

File tree

4 files changed

+194
-101
lines changed

4 files changed

+194
-101
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,58 +2840,65 @@ class UnsafeLibcFunctionCallGadget : public WarningGadget {
28402840
Handler->ignoreUnsafeBufferInLibcCall(Stmt->getBeginLoc()))
28412841
return false;
28422842

2843-
const CallExpr *Call = dyn_cast<CallExpr>(Stmt);
2844-
2845-
if (!Call)
2843+
auto *CE = dyn_cast<CallExpr>(Stmt);
2844+
if (!CE || !CE->getDirectCallee())
2845+
return false;
2846+
const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
2847+
if (!FD)
28462848
return false;
28472849

2848-
const FunctionDecl *CalleeDecl =
2849-
dyn_cast_or_null<FunctionDecl>(Call->getCalleeDecl());
2850+
bool IsGlobalAndNotInAnyNamespace =
2851+
FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
28502852

2851-
if (!CalleeDecl)
2853+
// A libc function must either be in the std:: namespace or a global
2854+
// function that is not in any namespace:
2855+
if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
28522856
return false;
2857+
2858+
// TO_UPSTREAM(BoundsSafety) ON
28532859
// If the call has a sole null-terminated argument, e.g., strlen,
28542860
// printf, atoi, we consider it safe:
2855-
if (hasNumArgs(Call, 1) && isNullTermPointer(Call->getArg(0), Ctx))
2861+
if (hasNumArgs(CE, 1) && isNullTermPointer(CE->getArg(0), Ctx))
28562862
return false;
2857-
if (!hasAnyBoundsAttributes(CalleeDecl)) {
2863+
if (!hasAnyBoundsAttributes(FD)) {
28582864
// Match a predefined unsafe libc function:
2859-
if (libc_func_matchers::isPredefinedUnsafeLibcFunc(*CalleeDecl)) {
2860-
Result.addNode(Tag, DynTypedNode::create(*Call));
2865+
if (libc_func_matchers::isPredefinedUnsafeLibcFunc(*FD)) {
2866+
Result.addNode(Tag, DynTypedNode::create(*CE));
28612867
return true;
28622868
}
28632869
} // v*printf and sprintf functions are always unsafe regardless of whether
28642870
// they have bounds annotations
2871+
// TO_UPSTREAM(BoundsSafety) OFF
28652872

28662873
// Match a call to one of the `v*printf` functions taking va-list, which
28672874
// cannot be checked at compile-time:
2868-
if (libc_func_matchers::isUnsafeVaListPrintfFunc(*CalleeDecl)) {
2869-
Result.addNode(UnsafeVaListTag, DynTypedNode::create(*CalleeDecl));
2870-
Result.addNode(Tag, DynTypedNode::create(*Call));
2875+
if (libc_func_matchers::isUnsafeVaListPrintfFunc(*FD)) {
2876+
Result.addNode(Tag, DynTypedNode::create(*CE));
2877+
Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
28712878
return true;
28722879
}
28732880

28742881
// Match a call to a `sprintf` function, which is never safe:
2875-
if (libc_func_matchers::isUnsafeSprintfFunc(*CalleeDecl)) {
2876-
Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*CalleeDecl));
2877-
Result.addNode(Tag, DynTypedNode::create(*Call));
2882+
if (libc_func_matchers::isUnsafeSprintfFunc(*FD)) {
2883+
Result.addNode(Tag, DynTypedNode::create(*CE));
2884+
Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
28782885
return true;
28792886
}
28802887

2881-
if (libc_func_matchers::isNormalPrintfFunc(*CalleeDecl)) {
2888+
if (libc_func_matchers::isNormalPrintfFunc(*FD)) {
28822889
// Match a call to an `snprintf` function. And first two arguments of the
28832890
// call (that describe a buffer) are not in safe patterns:
2884-
if (!hasAnyBoundsAttributes(CalleeDecl) &&
2885-
libc_func_matchers::hasUnsafeSnprintfBuffer(*Call, Ctx)) {
2886-
Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*Call));
2887-
Result.addNode(Tag, DynTypedNode::create(*Call));
2891+
if (!hasAnyBoundsAttributes(FD) &&
2892+
libc_func_matchers::hasUnsafeSnprintfBuffer(*CE, Ctx)) {
2893+
Result.addNode(Tag, DynTypedNode::create(*CE));
2894+
Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
28882895
return true;
28892896
}
28902897
// Match a call to a `printf` function, which can be safe if
28912898
// all arguments are null-terminated:
2892-
if (libc_func_matchers::hasUnsafePrintfStringArg(*Call, Ctx, Result,
2899+
if (libc_func_matchers::hasUnsafePrintfStringArg(*CE, Ctx, Result,
28932900
UnsafeStringTag)) {
2894-
Result.addNode(Tag, DynTypedNode::create(*Call));
2901+
Result.addNode(Tag, DynTypedNode::create(*CE));
28952902
return true;
28962903
}
28972904
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage -Wno-error=bounds-safety-strict-terminated-by-cast\
2+
// RUN: -verify -fexperimental-bounds-safety-attributes %s
3+
#include <ptrcheck.h>
4+
#include <stddef.h>
5+
6+
typedef struct {} FILE;
7+
typedef struct {} va_list;
8+
9+
namespace std {
10+
template <typename T>
11+
struct span {
12+
T *data() const noexcept;
13+
size_t size() const noexcept;
14+
size_t size_bytes() const noexcept;
15+
span<T> first(size_t count) const noexcept;
16+
span<T> last(size_t count) const noexcept;
17+
span<T> subspan(size_t offset, size_t count) const noexcept;
18+
const T &operator[](const size_t idx) const;
19+
};
20+
21+
template <typename CharT>
22+
struct basic_string {
23+
const CharT *data() const noexcept;
24+
CharT *data() noexcept;
25+
const CharT *c_str() const noexcept;
26+
size_t size() const noexcept;
27+
size_t length() const noexcept;
28+
};
29+
30+
typedef basic_string<char> string;
31+
typedef basic_string<wchar_t> wstring;
32+
}
33+
34+
// For libc functions that have annotations,
35+
// `-Wunsafe-buffer-usage-in-libc-call` yields to the interoperation
36+
// warnings.
37+
38+
// expected-note@+2{{consider using a safe container and passing '.data()' to the parameter 'dst' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'dst'}}
39+
// 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'}}
40+
void *memcpy(void * __sized_by(size) dst, const void * __sized_by(size) src, size_t size);
41+
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, ... );
44+
// 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'}}
45+
int snwprintf( wchar_t* __counted_by(buf_size) buffer, size_t buf_size, const wchar_t* format, ... );
46+
int vsnprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, va_list va_args);
47+
48+
// The '__counted_by(10)' is not a correct bounds annotation for
49+
// 'sprintf'. It is used to test that even if 'sprintf' has bounds
50+
// annotations, the function will still be warned against as 'sprintf'
51+
// can't be safe.
52+
int sprintf( char* __counted_by(10) buffer, const char* format, ... );
53+
54+
void test(char * p, char * q, const char * str,
55+
const char * __null_terminated safe_str,
56+
char * __counted_by(n) safe_p,
57+
size_t n,
58+
char * __counted_by(10) safe_ten) {
59+
memcpy(p, q, 10); // expected-warning2{{unsafe assignment to function parameter of count-attributed type}}
60+
snprintf(p, 10, "%s", "hlo"); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
61+
62+
// We still warn about unsafe string pointer arguments to printfs:
63+
snprintf(safe_p, n, "%s", str); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
64+
65+
memcpy(safe_p, safe_p, n); // no warn
66+
strlen(str); // expected-warning{{passing 'const char *' to parameter of incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation}}
67+
snprintf(safe_p, n, "%s", "hlo"); // no warn
68+
snprintf(safe_p, n, "%s", safe_str); // no warn
69+
70+
// v-printf functions and sprintf are still warned about because
71+
// they cannot be fully safe:
72+
va_list vlist;
73+
vsnprintf(safe_p, n, "%s", vlist); // expected-warning{{function 'vsnprintf' is unsafe}} expected-note{{'va_list' is unsafe}}
74+
sprintf(safe_ten, "%s", safe_str); // expected-warning{{function 'sprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
75+
76+
}
77+
78+
void test_wchar(wchar_t * p, wchar_t * q, const wchar_t * wstr,
79+
const wchar_t * __null_terminated safe_wstr,
80+
wchar_t * __null_terminated nt_wstr,
81+
wchar_t * __counted_by(n) safe_p,
82+
wchar_t * __sized_by(n) sizedby_p,
83+
size_t n) {
84+
std::wstring cxx_wstr;
85+
std::span<wchar_t> cxx_wspan;
86+
87+
snwprintf(safe_p, n, L"%ls", safe_wstr);
88+
snwprintf(cxx_wstr.data(), cxx_wstr.size(), cxx_wstr.c_str());
89+
snwprintf(cxx_wspan.data(), cxx_wspan.size(), cxx_wspan.data()); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
90+
snwprintf(p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
91+
snwprintf(sizedby_p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
92+
snwprintf(safe_p, n, L"%ls", wstr); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
93+
}
94+

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

Lines changed: 6 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -31,80 +31,16 @@ namespace std {
3131
typedef basic_string<wchar_t> wstring;
3232
}
3333

34-
namespace annotated_libc {
35-
// For libc functions that have annotations,
36-
// `-Wunsafe-buffer-usage-in-libc-call` yields to the interoperation
37-
// warnings.
38-
39-
// expected-note@+2{{consider using a safe container and passing '.data()' to the parameter 'dst' and '.size()' to its dependent parameter 'size' or 'std::span' and passing '.first(...).data()' to the parameter 'dst'}}
40-
// 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'}}
41-
void *memcpy(void * __sized_by(size) dst, const void * __sized_by(size) src, size_t size);
42-
size_t strlen( const char* __null_terminated str );
43-
// 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'}}
44-
int snprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, ... );
45-
// 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'}}
46-
int snwprintf( wchar_t* __counted_by(buf_size) buffer, size_t buf_size, const wchar_t* format, ... );
47-
int vsnprintf( char* __counted_by(buf_size) buffer, size_t buf_size, const char* format, va_list va_args);
48-
49-
// The '__counted_by(10)' is not a correct bounds annotation for
50-
// 'sprintf'. It is used to test that even if 'sprintf' has bounds
51-
// annotations, the function will still be warned against as 'sprintf'
52-
// can't be safe.
53-
int sprintf( char* __counted_by(10) buffer, const char* format, ... );
54-
55-
void test(char * p, char * q, const char * str,
56-
const char * __null_terminated safe_str,
57-
char * __counted_by(n) safe_p,
58-
size_t n,
59-
char * __counted_by(10) safe_ten) {
60-
memcpy(p, q, 10); // expected-warning2{{unsafe assignment to function parameter of count-attributed type}}
61-
snprintf(p, 10, "%s", "hlo"); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
62-
63-
// We still warn about unsafe string pointer arguments to printfs:
64-
snprintf(safe_p, n, "%s", str); // expected-warning{{function 'snprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
65-
66-
memcpy(safe_p, safe_p, n); // no warn
67-
strlen(str); // expected-warning{{passing 'const char *' to parameter of incompatible type 'const char * __terminated_by(0)' (aka 'const char *') is an unsafe operation}}
68-
snprintf(safe_p, n, "%s", "hlo"); // no warn
69-
snprintf(safe_p, n, "%s", safe_str); // no warn
70-
71-
// v-printf functions and sprintf are still warned about because
72-
// they cannot be fully safe:
73-
va_list vlist;
74-
vsnprintf(safe_p, n, "%s", vlist); // expected-warning{{function 'vsnprintf' is unsafe}} expected-note{{'va_list' is unsafe}}
75-
sprintf(safe_ten, "%s", safe_str); // expected-warning{{function 'sprintf' is unsafe}} expected-note{{change to 'snprintf' for explicit bounds checking}}
76-
77-
}
78-
79-
void test_wchar(wchar_t * p, wchar_t * q, const wchar_t * wstr,
80-
const wchar_t * __null_terminated safe_wstr,
81-
wchar_t * __null_terminated nt_wstr,
82-
wchar_t * __counted_by(n) safe_p,
83-
wchar_t * __sized_by(n) sizedby_p,
84-
size_t n) {
85-
std::wstring cxx_wstr;
86-
std::span<wchar_t> cxx_wspan;
87-
88-
snwprintf(safe_p, n, L"%ls", safe_wstr);
89-
snwprintf(cxx_wstr.data(), cxx_wstr.size(), cxx_wstr.c_str());
90-
snwprintf(cxx_wspan.data(), cxx_wspan.size(), cxx_wspan.data()); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
91-
snwprintf(p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
92-
snwprintf(sizedby_p, n, L"%ls", safe_wstr); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
93-
snwprintf(safe_p, n, L"%ls", wstr); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
94-
}
95-
} // namespace annotated_libc
96-
97-
namespace unannotated_libc {
98-
// The -Wunsafe-buffer-usage analysis considers some printf
99-
// functions safe, arguments are correctly annotated. Because these
100-
// functions are harder to be changed to C++ equivalents.
34+
// The -Wunsafe-buffer-usage analysis considers some printf
35+
// functions safe, arguments are correctly annotated. Because these
36+
// functions are harder to be changed to C++ equivalents.
10137
int printf(const char*, ... );
10238
int fprintf (FILE*, const char*, ... );
10339
int snprintf( char*, size_t, const char*, ... );
10440
int snwprintf( wchar_t*, size_t, const wchar_t*, ... );
10541
int vsnprintf( char*, size_t, const char*, va_list va_args );
106-
// It is convenient to accept functions like `strlen` or `atoi` when
107-
// they take a __null_termianted argument.
42+
// It is convenient to accept functions like `strlen` or `atoi` when
43+
// they take a __null_termianted argument.
10844
size_t strlen( const char* );
10945
int atoi( const char* );
11046

@@ -140,6 +76,5 @@ void test_wchar(const wchar_t * unsafe_wstr,
14076
snwprintf(cxx_wspan.data(), cxx_wspan.size(), cxx_wspan.data()); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
14177
snwprintf(sizedby_wp, n, safe_wstr); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{buffer pointer and size may not match}}
14278
snwprintf(safe_wp, n, unsafe_wstr); // expected-warning{{function 'snwprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
143-
14479
}
145-
} // namespace unannotated_libc
80+

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

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44
// RUN: -verify %s -x objective-c++
55
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
66
// RUN: -verify %s
7+
// RUN: %clang_cc1 -std=c++20 -Wno-all -Wunsafe-buffer-usage-in-libc-call \
8+
// RUN: -verify %s -DTEST_STD_NS
79

810
typedef struct {} FILE;
11+
typedef unsigned int size_t;
12+
13+
#ifdef TEST_STD_NS
14+
namespace std {
15+
#endif
16+
917
void memcpy();
1018
void __asan_memcpy();
1119
void strcpy();
@@ -25,6 +33,11 @@ int sscanf(const char * buffer, const char * format, ... );
2533
int wprintf(const wchar_t* format, ... );
2634
int __asan_printf();
2735

36+
#ifdef TEST_STD_NS
37+
} //namespace std
38+
using namespace std;
39+
#endif
40+
2841
namespace std {
2942
template< class InputIt, class OutputIt >
3043
OutputIt copy( InputIt first, InputIt last,
@@ -64,10 +77,6 @@ namespace std {
6477

6578
typedef basic_string_view<char> string_view;
6679
typedef basic_string_view<wchar_t> wstring_view;
67-
68-
// C function under std:
69-
void memcpy();
70-
void strcpy();
7180
}
7281

7382
void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
@@ -77,14 +86,16 @@ void f(char * p, char * q, std::span<char> s, std::span<char> s2) {
7786
aligned_char_ptr_t cp;
7887

7988
memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
80-
std::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
8189
__builtin_memcpy(p, q, 64); // expected-warning{{function '__builtin_memcpy' is unsafe}}
8290
__builtin___memcpy_chk(p, q, 8, 64); // expected-warning{{function '__builtin___memcpy_chk' is unsafe}}
8391
__asan_memcpy(); // expected-warning{{function '__asan_memcpy' is unsafe}}
8492
strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
85-
std::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
8693
strcpy_s(); // expected-warning{{function 'strcpy_s' is unsafe}}
8794
wcscpy_s(); // expected-warning{{function 'wcscpy_s' is unsafe}}
95+
#ifdef TEST_STD_NS
96+
std::strcpy(); // expected-warning{{function 'strcpy' is unsafe}}
97+
std::memcpy(); // expected-warning{{function 'memcpy' is unsafe}}
98+
#endif
8899

89100
/* Test printfs */
90101
fprintf((FILE*)p, "%s%d", p, *p); // expected-warning{{function 'fprintf' is unsafe}} expected-note{{string argument is not guaranteed to be null-terminated}}
@@ -184,13 +195,59 @@ void ff(char * p, char * q, std::span<char> s, std::span<char> s2) {
184195
#pragma clang diagnostic push
185196
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage-in-libc-call"
186197
memcpy();
187-
std::memcpy();
188198
__builtin_memcpy(p, q, 64);
189199
__builtin___memcpy_chk(p, q, 8, 64);
190200
__asan_memcpy();
191201
strcpy();
202+
#ifdef TEST_STD_NS
192203
std::strcpy();
204+
std::memcpy();
205+
#endif
193206
strcpy_s();
194207
wcscpy_s();
195208
#pragma clang diagnostic pop
196209
}
210+
211+
212+
213+
// functions not in global scope or std:: namespace are not libc
214+
// functions regardless of their names:
215+
struct StrBuff
216+
{
217+
void strcpy();
218+
void strcpy(char* dst);
219+
void memcpy(void *dst, const void *src, size_t size);
220+
};
221+
222+
namespace NS {
223+
void strcpy();
224+
void strcpy(char* dst);
225+
void memcpy(void *dst, const void *src, size_t size);
226+
}
227+
228+
namespace std {
229+
// class methods even in std namespace cannot be libc functions:
230+
struct LibC
231+
{
232+
void strcpy();
233+
void strcpy(char* dst);
234+
void memcpy(void *dst, const void *src, size_t size);
235+
};
236+
}
237+
238+
void test(StrBuff& str)
239+
{
240+
char buff[64];
241+
str.strcpy();
242+
str.strcpy(buff);
243+
str.memcpy(buff, buff, 64);
244+
NS::strcpy();
245+
NS::strcpy(buff);
246+
NS::memcpy(buff, buff, 64);
247+
248+
std::LibC LibC;
249+
250+
LibC.strcpy();
251+
LibC.strcpy(buff);
252+
LibC.memcpy(buff, buff, 64);
253+
}

0 commit comments

Comments
 (0)