[libc] Fix msan/asan memcpy reentrancy

This is needed to prevent asan/msan instrumentation to redirect CopyBlock to `__asan_memcpy` (resp. `__msan_memcpy`).
These functions would then differ operation to `memcpy` which leads to reentrancy issues.

With this patch, `memcpy` is fully instrumented and covered by asan/msan.

If this turns out to be too expensive, instrumentation can be selectively or fully disabled through the use of the `__attribute__((no_sanitize(address, memory)))` annotation.

Differential Revision: https://reviews.llvm.org/D99598

GitOrigin-RevId: 77d81c2270c6221a430aac17c2c0aa73b39cc0d2
diff --git a/src/__support/CMakeLists.txt b/src/__support/CMakeLists.txt
index 23ac214..b20e8bc 100644
--- a/src/__support/CMakeLists.txt
+++ b/src/__support/CMakeLists.txt
@@ -2,7 +2,7 @@
   common
   HDRS
     common.h
-    sanitizer_annotations.h
+    sanitizer.h
 )
 
 add_header_library(
diff --git a/src/__support/sanitizer.h b/src/__support/sanitizer.h
new file mode 100644
index 0000000..16fe6ec
--- /dev/null
+++ b/src/__support/sanitizer.h
@@ -0,0 +1,52 @@
+//===-- Convenient sanitizer macros ---------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SUPPORT_SANITIZER_H
+#define LLVM_LIBC_SRC_SUPPORT_SANITIZER_H
+
+#ifdef __has_feature
+#define LLVM_LIBC_HAVE_FEATURE(f) __has_feature(f)
+#else
+#define LLVM_LIBC_HAVE_FEATURE(f) 0
+#endif
+
+// MemorySanitizer (MSan) is a detector of uninitialized reads. It consists of
+// a compiler instrumentation module and a run-time library.
+#ifdef LLVM_LIBC_HAVE_MEMORY_SANITIZER
+#error "LLVM_LIBC_HAVE_MEMORY_SANITIZER cannot be directly set."
+#elif defined(MEMORY_SANITIZER)
+// The MEMORY_SANITIZER macro is deprecated but we will continue to honor it
+// for now.
+#define LLVM_LIBC_HAVE_MEMORY_SANITIZER 1
+#elif defined(__SANITIZE_MEMORY__)
+#define LLVM_LIBC_HAVE_MEMORY_SANITIZER 1
+#elif !defined(__native_client__) && LLVM_LIBC_HAVE_FEATURE(memory_sanitizer)
+#define LLVM_LIBC_HAVE_MEMORY_SANITIZER 1
+#endif
+
+// AddressSanitizer (ASan) is a fast memory error detector.
+#ifdef LLVM_LIBC_HAVE_ADDRESS_SANITIZER
+#error "LLVM_LIBC_HAVE_ADDRESS_SANITIZER cannot be directly set."
+#elif defined(ADDRESS_SANITIZER)
+// The ADDRESS_SANITIZER macro is deprecated but we will continue to honor it
+// for now.
+#define LLVM_LIBC_HAVE_ADDRESS_SANITIZER 1
+#elif defined(__SANITIZE_ADDRESS__)
+#define LLVM_LIBC_HAVE_ADDRESS_SANITIZER 1
+#elif LLVM_LIBC_HAVE_FEATURE(address_sanitizer)
+#define LLVM_LIBC_HAVE_ADDRESS_SANITIZER 1
+#endif
+
+#if LLVM_LIBC_HAVE_MEMORY_SANITIZER
+#include <sanitizer/msan_interface.h>
+#define SANITIZER_MEMORY_INITIALIZED(addr, size) __msan_unpoison(addr, size)
+#else
+#define SANITIZER_MEMORY_INITIALIZED(ptr, size)
+#endif
+
+#endif // LLVM_LIBC_SRC_SUPPORT_SANITIZER_H
diff --git a/src/__support/sanitizer_annotations.h b/src/__support/sanitizer_annotations.h
deleted file mode 100644
index ea1bb0a..0000000
--- a/src/__support/sanitizer_annotations.h
+++ /dev/null
@@ -1,19 +0,0 @@
-//===-- Convenient sanitizer annotations ----------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC_SUPPORT_SANITIZER_ANNOTATIONS_H
-#define LLVM_LIBC_SRC_SUPPORT_SANITIZER_ANNOTATIONS_H
-
-#if __has_feature(memory_sanitizer)
-#include <sanitizer/msan_interface.h>
-#define SANITIZER_MEMORY_INITIALIZED(addr, size) __msan_unpoison(addr, size)
-#else
-#define SANITIZER_MEMORY_INITIALIZED(ptr, size)
-#endif
-
-#endif // LLVM_LIBC_SRC_SUPPORT_SANITIZER_ANNOTATIONS_H
diff --git a/src/string/memory_utils/memcpy_utils.h b/src/string/memory_utils/memcpy_utils.h
index 44b8837..8fb0491 100644
--- a/src/string/memory_utils/memcpy_utils.h
+++ b/src/string/memory_utils/memcpy_utils.h
@@ -9,6 +9,7 @@
 #ifndef LIBC_SRC_STRING_MEMORY_UTILS_MEMCPY_UTILS_H
 #define LIBC_SRC_STRING_MEMORY_UTILS_MEMCPY_UTILS_H
 
+#include "src/__support/sanitizer.h"
 #include "src/string/memory_utils/utils.h"
 #include <stddef.h> // size_t
 
@@ -30,18 +31,28 @@
                                          const char *__restrict, size_t);
 #endif
 
+// Copies `kBlockSize` bytes from `src` to `dst` using a for loop.
+// This code requires the use of `-fno-buitin-memcpy` to prevent the compiler
+// from turning the for-loop back into `__builtin_memcpy`.
+template <size_t kBlockSize>
+static void ForLoopCopy(char *__restrict dst, const char *__restrict src) {
+  for (size_t i = 0; i < kBlockSize; ++i)
+    dst[i] = src[i];
+}
+
 // Copies `kBlockSize` bytes from `src` to `dst`.
 template <size_t kBlockSize>
 static void CopyBlock(char *__restrict dst, const char *__restrict src) {
 #if defined(LLVM_LIBC_MEMCPY_MONITOR)
   LLVM_LIBC_MEMCPY_MONITOR(dst, src, kBlockSize);
+#elif LLVM_LIBC_HAVE_MEMORY_SANITIZER || LLVM_LIBC_HAVE_ADDRESS_SANITIZER
+  ForLoopCopy<kBlockSize>(dst, src);
 #elif defined(USE_BUILTIN_MEMCPY_INLINE)
   __builtin_memcpy_inline(dst, src, kBlockSize);
 #elif defined(USE_BUILTIN_MEMCPY)
   __builtin_memcpy(dst, src, kBlockSize);
 #else
-  for (size_t i = 0; i < kBlockSize; ++i)
-    dst[i] = src[i];
+  ForLoopCopy<kBlockSize>(dst, src);
 #endif
 }
 
diff --git a/utils/FPUtil/x86_64/FEnv.h b/utils/FPUtil/x86_64/FEnv.h
index 1196aba..f654f0b 100644
--- a/utils/FPUtil/x86_64/FEnv.h
+++ b/utils/FPUtil/x86_64/FEnv.h
@@ -12,7 +12,7 @@
 #include <fenv.h>
 #include <stdint.h>
 
-#include "src/__support/sanitizer_annotations.h"
+#include "src/__support/sanitizer.h"
 
 namespace __llvm_libc {
 namespace fputil {