diff --git a/src/randomenv.cpp b/src/randomenv.cpp index 26fd643c6..945a92a2c 100644 --- a/src/randomenv.cpp +++ b/src/randomenv.cpp @@ -1,475 +1,517 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2019 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #if defined(HAVE_CONFIG_H) #include #endif #include #include +#include #include #include #include // for GetTime() #ifdef WIN32 #include // for Windows API #endif #include #include #include #include #include #include #include #ifndef WIN32 #include // must go before a number of other headers #include #include #include #include #include #include #include #include #endif #ifdef __MACH__ #include #include #include #endif #if HAVE_DECL_GETIFADDRS #include #endif #if HAVE_SYSCTL #include #if HAVE_VM_VM_PARAM_H #include #endif #if HAVE_SYS_RESOURCES_H #include #endif #if HAVE_SYS_VMMETER_H #include #endif #endif //! Necessary on some platforms extern char **environ; namespace { void RandAddSeedPerfmon(CSHA512 &hasher) { #ifdef WIN32 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom // Seed with the entire set of perfmon data // This can take up to 2 seconds, so only do it every 10 minutes static int64_t nLastPerfmon; if (GetTime() < nLastPerfmon + 10 * 60) { return; } nLastPerfmon = GetTime(); std::vector vData(250000, 0); long ret = 0; unsigned long nSize = 0; // Bail out at more than 10MB of performance data const size_t nMaxSize = 10000000; while (true) { nSize = vData.size(); ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) { break; } // Grow size of buffer exponentially vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); } RegCloseKey(HKEY_PERFORMANCE_DATA); if (ret == ERROR_SUCCESS) { hasher.Write(vData.data(), nSize); memory_cleanse(vData.data(), nSize); } else { // Performance data is only a best-effort attempt at improving the // situation when the OS randomness (and other sources) aren't // adequate. As a result, failure to read it is isn't considered // critical, so we don't call RandFailure(). // TODO: Add logging when the logger is made functional before global // constructors have been invoked. } #endif } /** Helper to easily feed data into a CSHA512. * * Note that this does not serialize the passed object (like stream.h's << * operators do). Its raw memory representation is used directly. */ template CSHA512 &operator<<(CSHA512 &hasher, const T &data) { static_assert( !std::is_same::type, char *>::value, "Calling operator<<(CSHA512, char*) is probably not what you want"); static_assert( !std::is_same::type, uint8_t *>::value, "Calling operator<<(CSHA512, uint8_t*) is probably not what you " "want"); static_assert( !std::is_same::type, const char *>::value, "Calling operator<<(CSHA512, const char*) is probably not what you " "want"); static_assert( !std::is_same::type, const uint8_t *>::value, "Calling operator<<(CSHA512, const uint8_t*) is " "probably not what you want"); hasher.Write((const uint8_t *)&data, sizeof(data)); return hasher; } #ifndef WIN32 void AddSockaddr(CSHA512 &hasher, const struct sockaddr *addr) { if (addr == nullptr) { return; } switch (addr->sa_family) { case AF_INET: hasher.Write((const uint8_t *)addr, sizeof(sockaddr_in)); break; case AF_INET6: hasher.Write((const uint8_t *)addr, sizeof(sockaddr_in6)); break; default: hasher.Write((const uint8_t *)&addr->sa_family, sizeof(addr->sa_family)); } } void AddFile(CSHA512 &hasher, const char *path) { struct stat sb = {}; int f = open(path, O_RDONLY); size_t total = 0; if (f != -1) { uint8_t fbuf[4096]; int n; hasher.Write((const uint8_t *)&f, sizeof(f)); if (fstat(f, &sb) == 0) { hasher << sb; } do { n = read(f, fbuf, sizeof(fbuf)); if (n > 0) { hasher.Write(fbuf, n); } total += n; /* not bothering with EINTR handling. */ } while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte close(f); } } void AddPath(CSHA512 &hasher, const char *path) { struct stat sb = {}; if (stat(path, &sb) == 0) { hasher.Write((const uint8_t *)path, strlen(path) + 1); hasher << sb; } } #endif #if HAVE_SYSCTL template void AddSysctl(CSHA512 &hasher) { int CTL[sizeof...(S)] = {S...}; uint8_t buffer[65536]; size_t siz = 65536; int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0); if (ret == 0 || (ret == -1 && errno == ENOMEM)) { hasher << sizeof(CTL); hasher.Write((const uint8_t *)CTL, sizeof(CTL)); if (siz > sizeof(buffer)) { siz = sizeof(buffer); } hasher << siz; hasher.Write(buffer, siz); } } #endif +#ifdef HAVE_GETCPUID +void inline AddCPUID(CSHA512 &hasher, uint32_t leaf, uint32_t subleaf, + uint32_t &ax, uint32_t &bx, uint32_t &cx, uint32_t &dx) { + GetCPUID(leaf, subleaf, ax, bx, cx, dx); + hasher << leaf << subleaf << ax << bx << cx << dx; +} + +void AddAllCPUID(CSHA512 &hasher) { + uint32_t ax, bx, cx, dx; + // Iterate over all standard leaves + // Returns max leaf in ax + AddCPUID(hasher, 0, 0, ax, bx, cx, dx); + uint32_t max = ax; + for (uint32_t leaf = 1; leaf <= max; ++leaf) { + for (uint32_t subleaf = 0;; ++subleaf) { + AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx); + // Iterate over subleaves for leaf 4, 11, 13 + if (leaf != 4 && leaf != 11 && leaf != 13) { + break; + } + if ((leaf == 4 || leaf == 13) && ax == 0) { + break; + } + if (leaf == 11 && (cx & 0xFF00) == 0) { + break; + } + } + } + // Iterate over all extended leaves + // Returns max extended leaf in ax + AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); + uint32_t ext_max = ax; + for (uint32_t leaf = 0x80000001; leaf <= ext_max; ++leaf) { + AddCPUID(hasher, leaf, 0, ax, bx, cx, dx); + } +} +#endif } // namespace void RandAddDynamicEnv(CSHA512 &hasher) { RandAddSeedPerfmon(hasher); // Various clocks #ifdef WIN32 FILETIME ftime; GetSystemTimeAsFileTime(&ftime); hasher << ftime; #else #ifndef __MACH__ // On non-MacOS systems, use various clock_gettime() calls. struct timespec ts = {}; #ifdef CLOCK_MONOTONIC clock_gettime(CLOCK_MONOTONIC, &ts); hasher << ts; #endif #ifdef CLOCK_REALTIME clock_gettime(CLOCK_REALTIME, &ts); hasher << ts; #endif #ifdef CLOCK_BOOTTIME clock_gettime(CLOCK_BOOTTIME, &ts); hasher << ts.tv_sec << ts.tv_nsec; #endif #else // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a // replacement for CLOCK_MONOTONIC, and clock_get_time for CALENDAR_CLOCK as // a replacement for CLOCK_REALTIME. hasher << mach_absolute_time(); // From https://gist.github.com/jbenet/1087739 clock_serv_t cclock; mach_timespec_t mts = {}; if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) { hasher << mts; mach_port_deallocate(mach_task_self(), cclock); } #endif // gettimeofday is available on all UNIX systems, but only has microsecond // precision. struct timeval tv = {}; gettimeofday(&tv, nullptr); hasher << tv; #endif // Probably redundant, but also use all the clocks C++11 provides: hasher << std::chrono::system_clock::now().time_since_epoch().count(); hasher << std::chrono::steady_clock::now().time_since_epoch().count(); hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count(); #ifndef WIN32 // Current resource usage. struct rusage usage = {}; if (getrusage(RUSAGE_SELF, &usage) == 0) { hasher << usage; } #endif #ifdef __linux__ AddFile(hasher, "/proc/diskstats"); AddFile(hasher, "/proc/vmstat"); AddFile(hasher, "/proc/schedstat"); AddFile(hasher, "/proc/zoneinfo"); AddFile(hasher, "/proc/meminfo"); AddFile(hasher, "/proc/softirqs"); AddFile(hasher, "/proc/stat"); AddFile(hasher, "/proc/self/schedstat"); AddFile(hasher, "/proc/self/status"); #endif #if HAVE_SYSCTL #ifdef CTL_KERN #if defined(KERN_PROC) && defined(KERN_PROC_ALL) AddSysctl(hasher); #endif #endif #ifdef CTL_HW #ifdef HW_DISKSTATS AddSysctl(hasher); #endif #endif #ifdef CTL_VM #ifdef VM_LOADAVG AddSysctl(hasher); #endif #ifdef VM_TOTAL AddSysctl(hasher); #endif #ifdef VM_METER AddSysctl(hasher); #endif #endif #endif // Stack and heap location void *addr = malloc(4097); hasher << &addr << addr; free(addr); } void RandAddStaticEnv(CSHA512 &hasher) { // Some compile-time static properties hasher << (CHAR_MIN < 0) << sizeof(void *) << sizeof(long) << sizeof(int); #if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__; #endif #ifdef _MSC_VER hasher << _MSC_VER; #endif hasher << __cplusplus; #ifdef _XOPEN_VERSION hasher << _XOPEN_VERSION; #endif #ifdef __VERSION__ const char *COMPILER_VERSION = __VERSION__; hasher.Write((const uint8_t *)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1); #endif // Bitcoin client version hasher << CLIENT_VERSION; +#ifdef HAVE_GETCPUID + AddAllCPUID(hasher); +#endif + // Memory locations hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ; // Hostname char hname[256]; if (gethostname(hname, 256) == 0) { hasher.Write((const uint8_t *)hname, strnlen(hname, 256)); } #if HAVE_DECL_GETIFADDRS // Network interfaces struct ifaddrs *ifad = NULL; getifaddrs(&ifad); struct ifaddrs *ifit = ifad; while (ifit != NULL) { hasher.Write((const uint8_t *)&ifit, sizeof(ifit)); hasher.Write((const uint8_t *)ifit->ifa_name, strlen(ifit->ifa_name) + 1); hasher.Write((const uint8_t *)&ifit->ifa_flags, sizeof(ifit->ifa_flags)); AddSockaddr(hasher, ifit->ifa_addr); AddSockaddr(hasher, ifit->ifa_netmask); AddSockaddr(hasher, ifit->ifa_dstaddr); ifit = ifit->ifa_next; } freeifaddrs(ifad); #endif #ifndef WIN32 // UNIX kernel information struct utsname name; if (uname(&name) != -1) { hasher.Write((const uint8_t *)&name.sysname, strlen(name.sysname) + 1); hasher.Write((const uint8_t *)&name.nodename, strlen(name.nodename) + 1); hasher.Write((const uint8_t *)&name.release, strlen(name.release) + 1); hasher.Write((const uint8_t *)&name.version, strlen(name.version) + 1); hasher.Write((const uint8_t *)&name.machine, strlen(name.machine) + 1); } /* Path and filesystem provided data */ AddPath(hasher, "/"); AddPath(hasher, "."); AddPath(hasher, "/tmp"); AddPath(hasher, "/home"); AddPath(hasher, "/proc"); #ifdef __linux__ AddFile(hasher, "/proc/cmdline"); AddFile(hasher, "/proc/cpuinfo"); AddFile(hasher, "/proc/version"); #endif AddFile(hasher, "/etc/passwd"); AddFile(hasher, "/etc/group"); AddFile(hasher, "/etc/hosts"); AddFile(hasher, "/etc/resolv.conf"); AddFile(hasher, "/etc/timezone"); AddFile(hasher, "/etc/localtime"); #endif // For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of // these will exist on every system. #if HAVE_SYSCTL #ifdef CTL_HW #ifdef HW_MACHINE AddSysctl(hasher); #endif #ifdef HW_MODEL AddSysctl(hasher); #endif #ifdef HW_NCPU AddSysctl(hasher); #endif #ifdef HW_PHYSMEM AddSysctl(hasher); #endif #ifdef HW_USERMEM AddSysctl(hasher); #endif #ifdef HW_MACHINE_ARCH AddSysctl(hasher); #endif #ifdef HW_REALMEM AddSysctl(hasher); #endif #ifdef HW_CPU_FREQ AddSysctl(hasher); #endif #ifdef HW_BUS_FREQ AddSysctl(hasher); #endif #ifdef HW_CACHELINE AddSysctl(hasher); #endif #endif #ifdef CTL_KERN #ifdef KERN_BOOTFILE AddSysctl(hasher); #endif #ifdef KERN_BOOTTIME AddSysctl(hasher); #endif #ifdef KERN_CLOCKRATE AddSysctl(hasher); #endif #ifdef KERN_HOSTID AddSysctl(hasher); #endif #ifdef KERN_HOSTUUID AddSysctl(hasher); #endif #ifdef KERN_HOSTNAME AddSysctl(hasher); #endif #ifdef KERN_OSRELDATE AddSysctl(hasher); #endif #ifdef KERN_OSRELEASE AddSysctl(hasher); #endif #ifdef KERN_OSREV AddSysctl(hasher); #endif #ifdef KERN_OSTYPE AddSysctl(hasher); #endif #ifdef KERN_POSIX1 AddSysctl(hasher); #endif #ifdef KERN_VERSION AddSysctl(hasher); #endif #endif #endif // Env variables if (environ) { for (size_t i = 0; environ[i]; ++i) { hasher.Write((const uint8_t *)environ[i], strlen(environ[i])); } } // Process, thread, user, session, group, ... ids. #ifdef WIN32 hasher << GetCurrentProcessId() << GetCurrentThreadId(); #else hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid(); #endif hasher << std::this_thread::get_id(); }