//===-- 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(const_cast(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(const_cast(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(s) % 16) == 0 && (n % 16) == 0) { u64 *p = reinterpret_cast(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(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(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(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; } wchar_t *internal_wcscpy(wchar_t *dst, const wchar_t *src) { wchar_t *dst_it = dst; do { *dst_it++ = *src++; } while (*src); 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; } wchar_t *internal_wcsncpy(wchar_t *dst, const wchar_t *src, uptr n) { uptr i; for (i = 0; i < n && src[i]; ++i) dst[i] = src[i]; internal_memset(dst + i, 0, (n - i) * sizeof(wchar_t)); 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(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(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(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