Skip to content

Commit dd230ef

Browse files
MaskRaytru
authored andcommitted
[sanitizer] Intercept glibc 2.38 __isoc23_* functions
`strtol("0b1", 0, 0)` can be (pre-C23) 0 or (C23) 1. `sscanf("0b10", "%i", &x)` is similar. glibc 2.38 introduced `__isoc23_strtol` and `__isoc23_scanf` family functions for binary compatibility. When `_ISOC2X_SOURCE` is defined (implied by `_GNU_SOURCE`) or `__STDC_VERSION__ > 201710L`, `__GLIBC_USE_ISOC2X` is defined to 1 and these `__isoc23_*` symbols are used. Add `__isoc23_` versions for the following interceptors: * sanitizer_common_interceptors.inc implements strtoimax/strtoumax. Remove incorrect FIXME about google/sanitizers#321 * asan_interceptors.cpp implements just strtol and strtoll. The default `replace_str` mode checks `nptr` is readable and `endptr` is writable. atoi reuses the existing strtol interceptor. * msan_interceptors.cpp implements strtol family functions and their `_l` versions. Tested by lib/msan/tests/msan_test.cpp * sanitizer_common_interceptors.inc implements scanf family functions. The strtol family functions are spreaded, which is not great, but the patch (intended for release/17.x) does not attempt to address the issue. Add symbols to lib/sanitizer_common/symbolizer/scripts/global_symbols.txt to support both glibc pre-2.38 and 2.38. When build bots migrate to glibc 2.38+, we will lose test coverage for non-isoc23 versions since the existing C++ unittests imply `_GNU_SOURCE`. Add test/sanitizer_common/TestCases/{strtol.c,scanf.c}. They catch msan false positive in the absence of the interceptors. Fix llvm#64388 Fix llvm#64946 Link: https://lists.gnu.org/archive/html/info-gnu/2023-07/msg00010.html ("The GNU C Library version 2.38 is now available") Reviewed By: #sanitizers, vitalybuka, mgorny Differential Revision: https://reviews.llvm.org/D158943 (cherry picked from commit ad7e250)
1 parent cf16374 commit dd230ef

File tree

6 files changed

+214
-38
lines changed

6 files changed

+214
-38
lines changed

compiler-rt/lib/asan/asan_interceptors.cpp

+28-22
Original file line numberDiff line numberDiff line change
@@ -588,19 +588,34 @@ INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
588588
return REAL(strncpy)(to, from, size);
589589
}
590590

591-
INTERCEPTOR(long, strtol, const char *nptr, char **endptr, int base) {
592-
void *ctx;
593-
ASAN_INTERCEPTOR_ENTER(ctx, strtol);
594-
ENSURE_ASAN_INITED();
595-
if (!flags()->replace_str) {
596-
return REAL(strtol)(nptr, endptr, base);
597-
}
591+
template <typename Fn>
592+
static ALWAYS_INLINE auto StrtolImpl(void *ctx, Fn real, const char *nptr,
593+
char **endptr, int base)
594+
-> decltype(real(nullptr, nullptr, 0)) {
595+
if (!flags()->replace_str)
596+
return real(nptr, endptr, base);
598597
char *real_endptr;
599-
long result = REAL(strtol)(nptr, &real_endptr, base);
598+
auto res = real(nptr, &real_endptr, base);
600599
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
601-
return result;
600+
return res;
602601
}
603602

603+
# define INTERCEPTOR_STRTO_BASE(ret_type, func) \
604+
INTERCEPTOR(ret_type, func, const char *nptr, char **endptr, int base) { \
605+
void *ctx; \
606+
ASAN_INTERCEPTOR_ENTER(ctx, func); \
607+
ENSURE_ASAN_INITED(); \
608+
return StrtolImpl(ctx, REAL(func), nptr, endptr, base); \
609+
}
610+
611+
INTERCEPTOR_STRTO_BASE(long, strtol)
612+
INTERCEPTOR_STRTO_BASE(long long, strtoll)
613+
614+
# if SANITIZER_GLIBC
615+
INTERCEPTOR_STRTO_BASE(long, __isoc23_strtol)
616+
INTERCEPTOR_STRTO_BASE(long long, __isoc23_strtoll)
617+
# endif
618+
604619
INTERCEPTOR(int, atoi, const char *nptr) {
605620
void *ctx;
606621
ASAN_INTERCEPTOR_ENTER(ctx, atoi);
@@ -639,19 +654,6 @@ INTERCEPTOR(long, atol, const char *nptr) {
639654
return result;
640655
}
641656

642-
INTERCEPTOR(long long, strtoll, const char *nptr, char **endptr, int base) {
643-
void *ctx;
644-
ASAN_INTERCEPTOR_ENTER(ctx, strtoll);
645-
ENSURE_ASAN_INITED();
646-
if (!flags()->replace_str) {
647-
return REAL(strtoll)(nptr, endptr, base);
648-
}
649-
char *real_endptr;
650-
long long result = REAL(strtoll)(nptr, &real_endptr, base);
651-
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
652-
return result;
653-
}
654-
655657
INTERCEPTOR(long long, atoll, const char *nptr) {
656658
void *ctx;
657659
ASAN_INTERCEPTOR_ENTER(ctx, atoll);
@@ -752,6 +754,10 @@ void InitializeAsanInterceptors() {
752754
ASAN_INTERCEPT_FUNC(atoll);
753755
ASAN_INTERCEPT_FUNC(strtol);
754756
ASAN_INTERCEPT_FUNC(strtoll);
757+
# if SANITIZER_GLIBC
758+
ASAN_INTERCEPT_FUNC(__isoc23_strtol);
759+
ASAN_INTERCEPT_FUNC(__isoc23_strtoll);
760+
# endif
755761

756762
// Intecept jump-related functions.
757763
ASAN_INTERCEPT_FUNC(longjmp);

compiler-rt/lib/msan/msan_interceptors.cpp

+37
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,25 @@ INTERCEPTORS_STRTO_BASE(long long, wcstoll, wchar_t)
464464
INTERCEPTORS_STRTO_BASE(unsigned long, wcstoul, wchar_t)
465465
INTERCEPTORS_STRTO_BASE(unsigned long long, wcstoull, wchar_t)
466466

467+
#if SANITIZER_GLIBC
468+
INTERCEPTORS_STRTO(double, __isoc23_strtod, char)
469+
INTERCEPTORS_STRTO(float, __isoc23_strtof, char)
470+
INTERCEPTORS_STRTO(long double, __isoc23_strtold, char)
471+
INTERCEPTORS_STRTO_BASE(long, __isoc23_strtol, char)
472+
INTERCEPTORS_STRTO_BASE(long long, __isoc23_strtoll, char)
473+
INTERCEPTORS_STRTO_BASE(unsigned long, __isoc23_strtoul, char)
474+
INTERCEPTORS_STRTO_BASE(unsigned long long, __isoc23_strtoull, char)
475+
INTERCEPTORS_STRTO_BASE(u64, __isoc23_strtouq, char)
476+
477+
INTERCEPTORS_STRTO(double, __isoc23_wcstod, wchar_t)
478+
INTERCEPTORS_STRTO(float, __isoc23_wcstof, wchar_t)
479+
INTERCEPTORS_STRTO(long double, __isoc23_wcstold, wchar_t)
480+
INTERCEPTORS_STRTO_BASE(long, __isoc23_wcstol, wchar_t)
481+
INTERCEPTORS_STRTO_BASE(long long, __isoc23_wcstoll, wchar_t)
482+
INTERCEPTORS_STRTO_BASE(unsigned long, __isoc23_wcstoul, wchar_t)
483+
INTERCEPTORS_STRTO_BASE(unsigned long long, __isoc23_wcstoull, wchar_t)
484+
#endif
485+
467486
#if SANITIZER_NETBSD
468487
#define INTERCEPT_STRTO(func) \
469488
INTERCEPT_FUNCTION(func); \
@@ -1748,6 +1767,24 @@ void InitializeInterceptors() {
17481767
INTERCEPT_STRTO(wcstoul);
17491768
INTERCEPT_STRTO(wcstoll);
17501769
INTERCEPT_STRTO(wcstoull);
1770+
#ifdef SANITIZER_GLIBC
1771+
INTERCEPT_STRTO(__isoc23_strtod);
1772+
INTERCEPT_STRTO(__isoc23_strtof);
1773+
INTERCEPT_STRTO(__isoc23_strtold);
1774+
INTERCEPT_STRTO(__isoc23_strtol);
1775+
INTERCEPT_STRTO(__isoc23_strtoul);
1776+
INTERCEPT_STRTO(__isoc23_strtoll);
1777+
INTERCEPT_STRTO(__isoc23_strtoull);
1778+
INTERCEPT_STRTO(__isoc23_strtouq);
1779+
INTERCEPT_STRTO(__isoc23_wcstod);
1780+
INTERCEPT_STRTO(__isoc23_wcstof);
1781+
INTERCEPT_STRTO(__isoc23_wcstold);
1782+
INTERCEPT_STRTO(__isoc23_wcstol);
1783+
INTERCEPT_STRTO(__isoc23_wcstoul);
1784+
INTERCEPT_STRTO(__isoc23_wcstoll);
1785+
INTERCEPT_STRTO(__isoc23_wcstoull);
1786+
#endif
1787+
17511788
#ifdef SANITIZER_NLDBL_VERSION
17521789
INTERCEPT_FUNCTION_VER(vswprintf, SANITIZER_NLDBL_VERSION);
17531790
INTERCEPT_FUNCTION_VER(swprintf, SANITIZER_NLDBL_VERSION);

compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc

+57-16
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,16 @@ VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap)
14911491

14921492
INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap)
14931493
VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap)
1494+
1495+
INTERCEPTOR(int, __isoc23_vscanf, const char *format, va_list ap)
1496+
VSCANF_INTERCEPTOR_IMPL(__isoc23_vscanf, false, format, ap)
1497+
1498+
INTERCEPTOR(int, __isoc23_vsscanf, const char *str, const char *format,
1499+
va_list ap)
1500+
VSCANF_INTERCEPTOR_IMPL(__isoc23_vsscanf, false, str, format, ap)
1501+
1502+
INTERCEPTOR(int, __isoc23_vfscanf, void *stream, const char *format, va_list ap)
1503+
VSCANF_INTERCEPTOR_IMPL(__isoc23_vfscanf, false, stream, format, ap)
14941504
#endif // SANITIZER_INTERCEPT_ISOC99_SCANF
14951505

14961506
INTERCEPTOR(int, scanf, const char *format, ...)
@@ -1511,6 +1521,15 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format)
15111521

15121522
INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...)
15131523
FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
1524+
1525+
INTERCEPTOR(int, __isoc23_scanf, const char *format, ...)
1526+
FORMAT_INTERCEPTOR_IMPL(__isoc23_scanf, __isoc23_vscanf, format)
1527+
1528+
INTERCEPTOR(int, __isoc23_fscanf, void *stream, const char *format, ...)
1529+
FORMAT_INTERCEPTOR_IMPL(__isoc23_fscanf, __isoc23_vfscanf, stream, format)
1530+
1531+
INTERCEPTOR(int, __isoc23_sscanf, const char *str, const char *format, ...)
1532+
FORMAT_INTERCEPTOR_IMPL(__isoc23_sscanf, __isoc23_vsscanf, str, format)
15141533
#endif
15151534

15161535
#endif
@@ -1534,7 +1553,13 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format)
15341553
COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \
15351554
COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \
15361555
COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \
1537-
COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf);
1556+
COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); \
1557+
COMMON_INTERCEPT_FUNCTION(__isoc23_scanf); \
1558+
COMMON_INTERCEPT_FUNCTION(__isoc23_sscanf); \
1559+
COMMON_INTERCEPT_FUNCTION(__isoc23_fscanf); \
1560+
COMMON_INTERCEPT_FUNCTION(__isoc23_vscanf); \
1561+
COMMON_INTERCEPT_FUNCTION(__isoc23_vsscanf); \
1562+
COMMON_INTERCEPT_FUNCTION(__isoc23_vfscanf);
15381563
#else
15391564
#define INIT_ISOC99_SCANF
15401565
#endif
@@ -3539,30 +3564,26 @@ UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
35393564
(real_endptr - nptr) + 1 : 0);
35403565
}
35413566

3542-
35433567
#if SANITIZER_INTERCEPT_STRTOIMAX
3544-
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
3545-
void *ctx;
3546-
COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base);
3547-
// FIXME: under ASan the call below may write to freed memory and corrupt
3548-
// its metadata. See
3549-
// https://github.com/google/sanitizers/issues/321.
3568+
template <typename Fn>
3569+
static ALWAYS_INLINE auto StrtoimaxImpl(void *ctx, Fn real, const char *nptr,
3570+
char **endptr, int base)
3571+
-> decltype(real(nullptr, nullptr, 0)) {
35503572
char *real_endptr;
3551-
INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
3573+
auto res = real(nptr, &real_endptr, base);
35523574
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
35533575
return res;
35543576
}
35553577

3578+
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
3579+
void *ctx;
3580+
COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base);
3581+
return StrtoimaxImpl(ctx, REAL(strtoimax), nptr, endptr, base);
3582+
}
35563583
INTERCEPTOR(UINTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
35573584
void *ctx;
35583585
COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base);
3559-
// FIXME: under ASan the call below may write to freed memory and corrupt
3560-
// its metadata. See
3561-
// https://github.com/google/sanitizers/issues/321.
3562-
char *real_endptr;
3563-
UINTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
3564-
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
3565-
return res;
3586+
return StrtoimaxImpl(ctx, REAL(strtoumax), nptr, endptr, base);
35663587
}
35673588

35683589
#define INIT_STRTOIMAX \
@@ -3572,6 +3593,25 @@ INTERCEPTOR(UINTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
35723593
#define INIT_STRTOIMAX
35733594
#endif
35743595

3596+
#if SANITIZER_INTERCEPT_STRTOIMAX && SANITIZER_GLIBC
3597+
INTERCEPTOR(INTMAX_T, __isoc23_strtoimax, const char *nptr, char **endptr, int base) {
3598+
void *ctx;
3599+
COMMON_INTERCEPTOR_ENTER(ctx, __isoc23_strtoimax, nptr, endptr, base);
3600+
return StrtoimaxImpl(ctx, REAL(__isoc23_strtoimax), nptr, endptr, base);
3601+
}
3602+
INTERCEPTOR(UINTMAX_T, __isoc23_strtoumax, const char *nptr, char **endptr, int base) {
3603+
void *ctx;
3604+
COMMON_INTERCEPTOR_ENTER(ctx, __isoc23_strtoumax, nptr, endptr, base);
3605+
return StrtoimaxImpl(ctx, REAL(__isoc23_strtoumax), nptr, endptr, base);
3606+
}
3607+
3608+
# define INIT_STRTOIMAX_C23 \
3609+
COMMON_INTERCEPT_FUNCTION(__isoc23_strtoimax); \
3610+
COMMON_INTERCEPT_FUNCTION(__isoc23_strtoumax);
3611+
#else
3612+
# define INIT_STRTOIMAX_C23
3613+
#endif
3614+
35753615
#if SANITIZER_INTERCEPT_MBSTOWCS
35763616
INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) {
35773617
void *ctx;
@@ -10304,6 +10344,7 @@ static void InitializeCommonInterceptors() {
1030410344
INIT_GETCWD;
1030510345
INIT_GET_CURRENT_DIR_NAME;
1030610346
INIT_STRTOIMAX;
10347+
INIT_STRTOIMAX_C23;
1030710348
INIT_MBSTOWCS;
1030810349
INIT_MBSNRTOWCS;
1030910350
INIT_WCSTOMBS;

compiler-rt/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt

+7
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ __interceptor_pthread_setspecific w
3434
__interceptor_read w
3535
__interceptor_realpath w
3636
__isinf U
37+
__isoc23_sscanf U
38+
__isoc23_strtol U
39+
__isoc23_strtoll U
40+
__isoc23_strtoll_l U
41+
__isoc23_strtoull U
42+
__isoc23_strtoull_l U
43+
__isoc23_vsscanf U
3744
__isoc99_sscanf U
3845
__isoc99_vsscanf U
3946
__moddi3 U
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %clang -std=c17 %s -o %t && %run %t
2+
/// Test __isoc23_* for glibc 2.38+.
3+
// RUN: %clang -std=c23 %s -o %t && %run %t
4+
5+
#include <assert.h>
6+
#include <stdarg.h>
7+
#include <stdio.h>
8+
9+
int test_vsscanf(const char *buf, const char *fmt, ...) {
10+
va_list ap;
11+
va_start(ap, fmt);
12+
int ret = vsscanf(buf, fmt, ap);
13+
va_end(ap);
14+
return ret;
15+
}
16+
17+
int main(int argc, char **argv) {
18+
int x, y;
19+
assert(sscanf("42", "%d", &x) == 1);
20+
assert(x == 42);
21+
assert(test_vsscanf("42", "%d", &y) == 1);
22+
assert(y == 42);
23+
return 0;
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %clang -std=c17 %s -o %t && %run %t
2+
/// Test __isoc23_* for glibc 2.38+.
3+
// RUN: %clang -std=c23 %s -o %t && %run %t
4+
5+
#include <assert.h>
6+
#include <inttypes.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <wchar.h>
10+
11+
#define TESTL(func) \
12+
{ \
13+
char *end; \
14+
long l = (long)func("42", &end, 0); \
15+
assert(l == 42); \
16+
assert(*end == '\0'); \
17+
}
18+
19+
#define TESTF(func) \
20+
{ \
21+
char *end; \
22+
long l = (long)func("42", &end); \
23+
assert(l == 42); \
24+
assert(*end == '\0'); \
25+
}
26+
27+
#define WTESTL(func) \
28+
{ \
29+
wchar_t *end; \
30+
long l = (long)func(L"42", &end, 0); \
31+
assert(l == 42); \
32+
assert(*end == L'\0'); \
33+
}
34+
35+
#define WTESTF(func) \
36+
{ \
37+
wchar_t *end; \
38+
long l = (long)func(L"42", &end); \
39+
assert(l == 42); \
40+
assert(*end == '\0'); \
41+
}
42+
43+
int main() {
44+
TESTL(strtol);
45+
TESTL(strtoll);
46+
TESTL(strtoimax);
47+
TESTL(strtoul);
48+
TESTL(strtoull);
49+
TESTL(strtoumax);
50+
TESTF(strtof);
51+
TESTF(strtod);
52+
TESTF(strtold);
53+
54+
WTESTL(wcstol);
55+
WTESTL(wcstoll);
56+
WTESTL(wcstoul);
57+
WTESTL(wcstoull);
58+
WTESTF(wcstof);
59+
WTESTF(wcstod);
60+
WTESTF(wcstold);
61+
}

0 commit comments

Comments
 (0)