Files
clang-p2996/compiler-rt/lib/sanitizer_common/sanitizer_libc.cpp
Marco Elver 0a71e25e24 [compiler-rt] Avoid memintrinsic calls inserted by the compiler
D135716 introduced -ftrivial-auto-var-init=pattern where supported.
Unfortunately this introduces unwanted memset() for large stack arrays,
as shown by the new tests added for asan and msan (tsan already had this
test).

In general, the problem of compiler-inserted memintrinsic calls
(memset/memcpy/memmove) is not new to compiler-rt, and has been a
problem before.

To avoid introducing unwanted memintrinsic calls, we redefine
memintrinsics as __sanitizer_internal_mem* at the assembly level for
most source files automatically (where sanitizer_common_internal_defs.h
is included).

In few cases, redefining a symbol in this way causes issues for
interceptors, namely the memintrinsic interceptor themselves. For such
source files we have to selectively disable the redefinition.

Other alternatives have been considered, but simply do not work well in
the context of compiler-rt:

	1. Linker --wrap:  this does not work because --wrap only
	   applies to the final link, and would not apply when building
	   sanitizer static libraries.

	2. Changing references to memset() via objcopy:  this may work,
	   but due to the complexities of the build system, introducing
	   such a post-processing step for the right object files (in
	   particular object files defining memset cannot be touched)
	   seems infeasible.

The chosen solution works well (as shown by the tests). Other libraries
have chosen the same solution where nothing else works (see e.g. glibc's
"symbol-hacks.h").

v4:
- Add interface attribute to __sanitizer_internal_mem* declarations as
  well, as otherwise some compilers (MSVC) will complain.
- Add SANITIZER_COMMON_NO_REDEFINE_BUILTINS to source files using
  C++STL, since this could lead to ODR violations (see added comment).

v3:
- Don't use ALIAS() to alias internal_mem*() functions to
  __sanitizer_internal_mem*() functions, but just define them as
  ALWAYS_INLINE functions instead. This will work on darwin and windows.

v2:
- Fix ubsan_minimal build where compiler decides to insert
  memset/memcpy: ubsan_minimal has work without RTSanitizerCommonLibc,
  therefore do not redefine the builtins.
- Fix definition of internal_mem* functions with compilers that want the
  aliased function to already be defined before.
- Fix definition of __sanitizer_internal_mem* functions with compilers
  more pedantic about attribute placement around extern "C".

Reviewed By: vitalybuka, dvyukov

Differential Revision: https://reviews.llvm.org/D151152
2023-06-06 16:10:45 +02:00

302 lines
7.9 KiB
C++

//===-- sanitizer_libc.cpp ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries. See sanitizer_libc.h for details.
//===----------------------------------------------------------------------===//
// Do not redefine builtins; this file is defining the builtin replacements.
#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
#include "sanitizer_allocator_internal.h"
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
namespace __sanitizer {
s64 internal_atoll(const char *nptr) {
return internal_simple_strtoll(nptr, nullptr, 10);
}
void *internal_memchr(const void *s, int c, uptr n) {
const char *t = (const char *)s;
for (uptr i = 0; i < n; ++i, ++t)
if (*t == c)
return reinterpret_cast<void *>(const_cast<char *>(t));
return nullptr;
}
void *internal_memrchr(const void *s, int c, uptr n) {
const char *t = (const char *)s;
void *res = nullptr;
for (uptr i = 0; i < n; ++i, ++t) {
if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
}
return res;
}
int internal_memcmp(const void* s1, const void* s2, uptr n) {
const char *t1 = (const char *)s1;
const char *t2 = (const char *)s2;
for (uptr i = 0; i < n; ++i, ++t1, ++t2)
if (*t1 != *t2)
return *t1 < *t2 ? -1 : 1;
return 0;
}
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memcpy(void *dest,
const void *src,
uptr n) {
char *d = (char*)dest;
const char *s = (const char *)src;
for (uptr i = 0; i < n; ++i)
d[i] = s[i];
return dest;
}
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memmove(
void *dest, const void *src, uptr n) {
char *d = (char*)dest;
const char *s = (const char *)src;
sptr i, signed_n = (sptr)n;
CHECK_GE(signed_n, 0);
if (d < s) {
for (i = 0; i < signed_n; ++i)
d[i] = s[i];
} else {
if (d > s && signed_n > 0) {
for (i = signed_n - 1; i >= 0; --i) {
d[i] = s[i];
}
}
}
return dest;
}
SANITIZER_INTERFACE_ATTRIBUTE void *__sanitizer_internal_memset(void *s, int c,
uptr n) {
// Optimize for the most performance-critical case:
if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
u64 *p = reinterpret_cast<u64*>(s);
u64 *e = p + n / 8;
u64 v = c;
v |= v << 8;
v |= v << 16;
v |= v << 32;
for (; p < e; p += 2)
p[0] = p[1] = v;
return s;
}
// The next line prevents Clang from making a call to memset() instead of the
// loop below.
// FIXME: building the runtime with -ffreestanding is a better idea. However
// there currently are linktime problems due to PR12396.
char volatile *t = (char*)s;
for (uptr i = 0; i < n; ++i, ++t) {
*t = c;
}
return s;
}
} // extern "C"
uptr internal_strcspn(const char *s, const char *reject) {
uptr i;
for (i = 0; s[i]; i++) {
if (internal_strchr(reject, s[i]))
return i;
}
return i;
}
char* internal_strdup(const char *s) {
uptr len = internal_strlen(s);
char *s2 = (char*)InternalAlloc(len + 1);
internal_memcpy(s2, s, len);
s2[len] = 0;
return s2;
}
int internal_strcmp(const char *s1, const char *s2) {
while (true) {
unsigned c1 = *s1;
unsigned c2 = *s2;
if (c1 != c2) return (c1 < c2) ? -1 : 1;
if (c1 == 0) break;
s1++;
s2++;
}
return 0;
}
int internal_strncmp(const char *s1, const char *s2, uptr n) {
for (uptr i = 0; i < n; i++) {
unsigned c1 = *s1;
unsigned c2 = *s2;
if (c1 != c2) return (c1 < c2) ? -1 : 1;
if (c1 == 0) break;
s1++;
s2++;
}
return 0;
}
char* internal_strchr(const char *s, int c) {
while (true) {
if (*s == (char)c)
return const_cast<char *>(s);
if (*s == 0)
return nullptr;
s++;
}
}
char *internal_strchrnul(const char *s, int c) {
char *res = internal_strchr(s, c);
if (!res)
res = const_cast<char *>(s) + internal_strlen(s);
return res;
}
char *internal_strrchr(const char *s, int c) {
const char *res = nullptr;
for (uptr i = 0; s[i]; i++) {
if (s[i] == c) res = s + i;
}
return const_cast<char *>(res);
}
uptr internal_strlen(const char *s) {
uptr i = 0;
while (s[i]) i++;
return i;
}
uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
const uptr srclen = internal_strlen(src);
const uptr dstlen = internal_strnlen(dst, maxlen);
if (dstlen == maxlen) return maxlen + srclen;
if (srclen < maxlen - dstlen) {
internal_memmove(dst + dstlen, src, srclen + 1);
} else {
internal_memmove(dst + dstlen, src, maxlen - dstlen - 1);
dst[maxlen - 1] = '\0';
}
return dstlen + srclen;
}
char *internal_strncat(char *dst, const char *src, uptr n) {
uptr len = internal_strlen(dst);
uptr i;
for (i = 0; i < n && src[i]; i++)
dst[len + i] = src[i];
dst[len + i] = 0;
return dst;
}
uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
const uptr srclen = internal_strlen(src);
if (srclen < maxlen) {
internal_memmove(dst, src, srclen + 1);
} else if (maxlen != 0) {
internal_memmove(dst, src, maxlen - 1);
dst[maxlen - 1] = '\0';
}
return srclen;
}
char *internal_strncpy(char *dst, const char *src, uptr n) {
uptr i;
for (i = 0; i < n && src[i]; i++)
dst[i] = src[i];
internal_memset(dst + i, '\0', n - i);
return dst;
}
uptr internal_strnlen(const char *s, uptr maxlen) {
uptr i = 0;
while (i < maxlen && s[i]) i++;
return i;
}
char *internal_strstr(const char *haystack, const char *needle) {
// This is O(N^2), but we are not using it in hot places.
uptr len1 = internal_strlen(haystack);
uptr len2 = internal_strlen(needle);
if (len1 < len2) return nullptr;
for (uptr pos = 0; pos <= len1 - len2; pos++) {
if (internal_memcmp(haystack + pos, needle, len2) == 0)
return const_cast<char *>(haystack) + pos;
}
return nullptr;
}
s64 internal_simple_strtoll(const char *nptr, const char **endptr, int base) {
CHECK_EQ(base, 10);
while (IsSpace(*nptr)) nptr++;
int sgn = 1;
u64 res = 0;
bool have_digits = false;
char *old_nptr = const_cast<char *>(nptr);
if (*nptr == '+') {
sgn = 1;
nptr++;
} else if (*nptr == '-') {
sgn = -1;
nptr++;
}
while (IsDigit(*nptr)) {
res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;
int digit = ((*nptr) - '0');
res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;
have_digits = true;
nptr++;
}
if (endptr) {
*endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
}
if (sgn > 0) {
return (s64)(Min((u64)INT64_MAX, res));
} else {
return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);
}
}
uptr internal_wcslen(const wchar_t *s) {
uptr i = 0;
while (s[i]) i++;
return i;
}
uptr internal_wcsnlen(const wchar_t *s, uptr maxlen) {
uptr i = 0;
while (i < maxlen && s[i]) i++;
return i;
}
bool mem_is_zero(const char *beg, uptr size) {
CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check.
const char *end = beg + size;
uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
uptr all = 0;
// Prologue.
for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
all |= *mem;
// Aligned loop.
for (; aligned_beg < aligned_end; aligned_beg++)
all |= *aligned_beg;
// Epilogue.
if ((char *)aligned_end >= beg) {
for (const char *mem = (char *)aligned_end; mem < end; mem++) all |= *mem;
}
return all == 0;
}
} // namespace __sanitizer