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.
26namespace __sanitizer {
27
28extern int VSNPrintf(char *buff, int buff_length, const char *format,
29                     va_list args);
30
31} // namespace __sanitizer
32
33namespace __scudo {
34
35FORMAT(1, 2)
36void 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
47typedef struct {
48  u32 Eax;
49  u32 Ebx;
50  u32 Ecx;
51  u32 Edx;
52} CPUIDInfo;
53
54static 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"
63static 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
81bool 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.
104static 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
121Xorshift128Plus::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