1 //===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// Platform specific utility functions.
10 ///
11 //===----------------------------------------------------------------------===//
12 
13 #include "scudo_utils.h"
14 
15 #if defined(__x86_64__) || defined(__i386__)
16 # include <cpuid.h>
17 #elif defined(__arm__) || defined(__aarch64__)
18 # include "sanitizer_common/sanitizer_getauxval.h"
19 # if SANITIZER_FUCHSIA
20 #  include <zircon/syscalls.h>
21 #  include <zircon/features.h>
22 # elif SANITIZER_POSIX
23 #  include "sanitizer_common/sanitizer_posix.h"
24 #  include <fcntl.h>
25 # endif
26 #endif
27 
28 #include <stdarg.h>
29 
30 // TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less
31 //                complicated string formatting code. The following is a
32 //                temporary workaround to be able to use __sanitizer::VSNPrintf.
33 namespace __sanitizer {
34 
35 extern int VSNPrintf(char *buff, int buff_length, const char *format,
36                      va_list args);
37 
38 }  // namespace __sanitizer
39 
40 namespace __scudo {
41 
dieWithMessage(const char * Format,...)42 FORMAT(1, 2) void NORETURN dieWithMessage(const char *Format, ...) {
43   static const char ScudoError[] = "Scudo ERROR: ";
44   static constexpr uptr PrefixSize = sizeof(ScudoError) - 1;
45   // Our messages are tiny, 256 characters is more than enough.
46   char Message[256];
47   va_list Args;
48   va_start(Args, Format);
49   internal_memcpy(Message, ScudoError, PrefixSize);
50   VSNPrintf(Message + PrefixSize, sizeof(Message) - PrefixSize, Format, Args);
51   va_end(Args);
52   LogMessageOnPrintf(Message);
53   if (common_flags()->abort_on_error)
54     SetAbortMessage(Message);
55   RawWrite(Message);
56   Die();
57 }
58 
59 #if defined(__x86_64__) || defined(__i386__)
60 // i386 and x86_64 specific code to detect CRC32 hardware support via CPUID.
61 // CRC32 requires the SSE 4.2 instruction set.
62 # ifndef bit_SSE4_2
63 #  define bit_SSE4_2 bit_SSE42  // clang and gcc have different defines.
64 # endif
65 
66 #ifndef signature_HYGON_ebx // They are not defined in gcc.
67 // HYGON: "HygonGenuine".
68 #define signature_HYGON_ebx 0x6f677948
69 #define signature_HYGON_edx 0x6e65476e
70 #define signature_HYGON_ecx 0x656e6975
71 #endif
72 
hasHardwareCRC32()73 bool hasHardwareCRC32() {
74   u32 Eax, Ebx, Ecx, Edx;
75   __get_cpuid(0, &Eax, &Ebx, &Ecx, &Edx);
76   const bool IsIntel = (Ebx == signature_INTEL_ebx) &&
77                        (Edx == signature_INTEL_edx) &&
78                        (Ecx == signature_INTEL_ecx);
79   const bool IsAMD = (Ebx == signature_AMD_ebx) &&
80                      (Edx == signature_AMD_edx) &&
81                      (Ecx == signature_AMD_ecx);
82   const bool IsHygon = (Ebx == signature_HYGON_ebx) &&
83                        (Edx == signature_HYGON_edx) &&
84                        (Ecx == signature_HYGON_ecx);
85   if (!IsIntel && !IsAMD && !IsHygon)
86     return false;
87   __get_cpuid(1, &Eax, &Ebx, &Ecx, &Edx);
88   return !!(Ecx & bit_SSE4_2);
89 }
90 #elif defined(__arm__) || defined(__aarch64__)
91 // For ARM and AArch64, hardware CRC32 support is indicated in the AT_HWCAP
92 // auxiliary vector.
93 # ifndef AT_HWCAP
94 #  define AT_HWCAP 16
95 # endif
96 # ifndef HWCAP_CRC32
97 #  define HWCAP_CRC32 (1 << 7)  // HWCAP_CRC32 is missing on older platforms.
98 # endif
99 # if SANITIZER_POSIX
hasHardwareCRC32ARMPosix()100 bool hasHardwareCRC32ARMPosix() {
101   uptr F = internal_open("/proc/self/auxv", O_RDONLY);
102   if (internal_iserror(F))
103     return false;
104   struct { uptr Tag; uptr Value; } Entry = { 0, 0 };
105   for (;;) {
106     uptr N = internal_read(F, &Entry, sizeof(Entry));
107     if (internal_iserror(N) || N != sizeof(Entry) ||
108         (Entry.Tag == 0 && Entry.Value == 0) || Entry.Tag == AT_HWCAP)
109       break;
110   }
111   internal_close(F);
112   return (Entry.Tag == AT_HWCAP && (Entry.Value & HWCAP_CRC32) != 0);
113 }
114 # else
hasHardwareCRC32ARMPosix()115 bool hasHardwareCRC32ARMPosix() { return false; }
116 # endif  // SANITIZER_POSIX
117 
118 // Bionic doesn't initialize its globals early enough. This causes issues when
119 // trying to access them from a preinit_array (b/25751302) or from another
120 // constructor called before the libc one (b/68046352). __progname is
121 // initialized after the other globals, so we can check its value to know if
122 // calling getauxval is safe.
123 extern "C" SANITIZER_WEAK_ATTRIBUTE char *__progname;
areBionicGlobalsInitialized()124 INLINE bool areBionicGlobalsInitialized() {
125   return !SANITIZER_ANDROID || (&__progname && __progname);
126 }
127 
hasHardwareCRC32()128 bool hasHardwareCRC32() {
129 #if SANITIZER_FUCHSIA
130   u32 HWCap;
131   zx_status_t Status = zx_system_get_features(ZX_FEATURE_KIND_CPU, &HWCap);
132   if (Status != ZX_OK || (HWCap & ZX_ARM64_FEATURE_ISA_CRC32) == 0)
133     return false;
134   return true;
135 #else
136   if (&getauxval && areBionicGlobalsInitialized())
137     return !!(getauxval(AT_HWCAP) & HWCAP_CRC32);
138   return hasHardwareCRC32ARMPosix();
139 #endif  // SANITIZER_FUCHSIA
140 }
141 #else
hasHardwareCRC32()142 bool hasHardwareCRC32() { return false; }
143 #endif  // defined(__x86_64__) || defined(__i386__)
144 
145 }  // namespace __scudo
146