Home | History | Annotate | Download | only in scudo
      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)
     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 
     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"
     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 
     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.
    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
    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