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