1 //===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// Platform specific utility functions.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "scudo_utils.h"
15
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdarg.h>
19 #include <unistd.h>
20
21 #include <cstring>
22
23 // TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less
24 // complicated string formatting code. The following is a
25 // temporary workaround to be able to use __sanitizer::VSNPrintf.
26 namespace __sanitizer {
27
28 extern int VSNPrintf(char *buff, int buff_length, const char *format,
29 va_list args);
30
31 } // namespace __sanitizer
32
33 namespace __scudo {
34
35 FORMAT(1, 2)
dieWithMessage(const char * Format,...)36 void dieWithMessage(const char *Format, ...) {
37 // Our messages are tiny, 128 characters is more than enough.
38 char Message[128];
39 va_list Args;
40 va_start(Args, Format);
41 __sanitizer::VSNPrintf(Message, sizeof(Message), Format, Args);
42 va_end(Args);
43 RawWrite(Message);
44 Die();
45 }
46
47 typedef struct {
48 u32 Eax;
49 u32 Ebx;
50 u32 Ecx;
51 u32 Edx;
52 } CPUIDInfo;
53
getCPUID(CPUIDInfo * info,u32 leaf,u32 subleaf)54 static void getCPUID(CPUIDInfo *info, u32 leaf, u32 subleaf)
55 {
56 asm volatile("cpuid"
57 : "=a" (info->Eax), "=b" (info->Ebx), "=c" (info->Ecx), "=d" (info->Edx)
58 : "a" (leaf), "c" (subleaf)
59 );
60 }
61
62 // Returns true is the CPU is a "GenuineIntel" or "AuthenticAMD"
isSupportedCPU()63 static bool isSupportedCPU()
64 {
65 CPUIDInfo Info;
66
67 getCPUID(&Info, 0, 0);
68 if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Genu", 4) == 0 &&
69 memcmp(reinterpret_cast<char *>(&Info.Edx), "ineI", 4) == 0 &&
70 memcmp(reinterpret_cast<char *>(&Info.Ecx), "ntel", 4) == 0) {
71 return true;
72 }
73 if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Auth", 4) == 0 &&
74 memcmp(reinterpret_cast<char *>(&Info.Edx), "enti", 4) == 0 &&
75 memcmp(reinterpret_cast<char *>(&Info.Ecx), "cAMD", 4) == 0) {
76 return true;
77 }
78 return false;
79 }
80
testCPUFeature(CPUFeature feature)81 bool testCPUFeature(CPUFeature feature)
82 {
83 static bool InfoInitialized = false;
84 static CPUIDInfo CPUInfo = {};
85
86 if (InfoInitialized == false) {
87 if (isSupportedCPU() == true)
88 getCPUID(&CPUInfo, 1, 0);
89 else
90 UNIMPLEMENTED();
91 InfoInitialized = true;
92 }
93 switch (feature) {
94 case SSE4_2:
95 return ((CPUInfo.Ecx >> 20) & 0x1) != 0;
96 default:
97 break;
98 }
99 return false;
100 }
101
102 // readRetry will attempt to read Count bytes from the Fd specified, and if
103 // interrupted will retry to read additional bytes to reach Count.
readRetry(int Fd,u8 * Buffer,size_t Count)104 static ssize_t readRetry(int Fd, u8 *Buffer, size_t Count) {
105 ssize_t AmountRead = 0;
106 while (static_cast<size_t>(AmountRead) < Count) {
107 ssize_t Result = read(Fd, Buffer + AmountRead, Count - AmountRead);
108 if (Result > 0)
109 AmountRead += Result;
110 else if (!Result)
111 break;
112 else if (errno != EINTR) {
113 AmountRead = -1;
114 break;
115 }
116 }
117 return AmountRead;
118 }
119
120 // Default constructor for Xorshift128Plus seeds the state with /dev/urandom
Xorshift128Plus()121 Xorshift128Plus::Xorshift128Plus() {
122 int Fd = open("/dev/urandom", O_RDONLY);
123 bool Success = readRetry(Fd, reinterpret_cast<u8 *>(&State_0_),
124 sizeof(State_0_)) == sizeof(State_0_);
125 Success &= readRetry(Fd, reinterpret_cast<u8 *>(&State_1_),
126 sizeof(State_1_)) == sizeof(State_1_);
127 close(Fd);
128 if (!Success) {
129 dieWithMessage("ERROR: failed to read enough data from /dev/urandom.\n");
130 }
131 }
132
133 } // namespace __scudo
134