//===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Platform specific utility functions. /// //===----------------------------------------------------------------------===// #include "scudo_utils.h" #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <unistd.h> #include <cstring> // TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less // complicated string formatting code. The following is a // temporary workaround to be able to use __sanitizer::VSNPrintf. namespace __sanitizer { extern int VSNPrintf(char *buff, int buff_length, const char *format, va_list args); } // namespace __sanitizer namespace __scudo { FORMAT(1, 2) void dieWithMessage(const char *Format, ...) { // Our messages are tiny, 128 characters is more than enough. char Message[128]; va_list Args; va_start(Args, Format); __sanitizer::VSNPrintf(Message, sizeof(Message), Format, Args); va_end(Args); RawWrite(Message); Die(); } typedef struct { u32 Eax; u32 Ebx; u32 Ecx; u32 Edx; } CPUIDInfo; static void getCPUID(CPUIDInfo *info, u32 leaf, u32 subleaf) { asm volatile("cpuid" : "=a" (info->Eax), "=b" (info->Ebx), "=c" (info->Ecx), "=d" (info->Edx) : "a" (leaf), "c" (subleaf) ); } // Returns true is the CPU is a "GenuineIntel" or "AuthenticAMD" static bool isSupportedCPU() { CPUIDInfo Info; getCPUID(&Info, 0, 0); if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Genu", 4) == 0 && memcmp(reinterpret_cast<char *>(&Info.Edx), "ineI", 4) == 0 && memcmp(reinterpret_cast<char *>(&Info.Ecx), "ntel", 4) == 0) { return true; } if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Auth", 4) == 0 && memcmp(reinterpret_cast<char *>(&Info.Edx), "enti", 4) == 0 && memcmp(reinterpret_cast<char *>(&Info.Ecx), "cAMD", 4) == 0) { return true; } return false; } bool testCPUFeature(CPUFeature feature) { static bool InfoInitialized = false; static CPUIDInfo CPUInfo = {}; if (InfoInitialized == false) { if (isSupportedCPU() == true) getCPUID(&CPUInfo, 1, 0); else UNIMPLEMENTED(); InfoInitialized = true; } switch (feature) { case SSE4_2: return ((CPUInfo.Ecx >> 20) & 0x1) != 0; default: break; } return false; } // readRetry will attempt to read Count bytes from the Fd specified, and if // interrupted will retry to read additional bytes to reach Count. static ssize_t readRetry(int Fd, u8 *Buffer, size_t Count) { ssize_t AmountRead = 0; while (static_cast<size_t>(AmountRead) < Count) { ssize_t Result = read(Fd, Buffer + AmountRead, Count - AmountRead); if (Result > 0) AmountRead += Result; else if (!Result) break; else if (errno != EINTR) { AmountRead = -1; break; } } return AmountRead; } // Default constructor for Xorshift128Plus seeds the state with /dev/urandom Xorshift128Plus::Xorshift128Plus() { int Fd = open("/dev/urandom", O_RDONLY); bool Success = readRetry(Fd, reinterpret_cast<u8 *>(&State_0_), sizeof(State_0_)) == sizeof(State_0_); Success &= readRetry(Fd, reinterpret_cast<u8 *>(&State_1_), sizeof(State_1_)) == sizeof(State_1_); close(Fd); if (!Success) { dieWithMessage("ERROR: failed to read enough data from /dev/urandom.\n"); } } } // namespace __scudo