From 1b75f4f1d7fd4dba17ff811965aec377bba8df2a Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Fri, 1 Mar 2019 13:54:15 -0500 Subject: [PATCH 1/3] Merge #15485: add rpc_misc.py, mv test getmemoryinfo, add test mallocinfo f13ad1cae0 modify test for memory locked in case locking pages failed at some point (Adam Jonas) 2fa85ebd1c add rpc_misc.py, mv test getmemoryinfo, add test mallocinfo (Adam Jonas) Pull request description: Creating the `rpc_misc.py` functional test file to add space for adding tests to a file that doesn't have a lot of coverage. - Removing the `getmemoryinfo()` smoke test from wallet basic rather than moving it to keep the wallet decoupled. Feel like testing for reasonable memory allocation values should suffice. - Adding coverage for `mallocinfo()`. Introduced standard lib XML parser since the function exports an XML string that describes the current state of the memory-allocation implementation in the caller. Tree-SHA512: ced30115622916c88d1e729969ee331272ec9f2881eb36dee4bb7331bf633a6810a57fed63a0cfaf86de698edb5162e6a035efd07c89ece1df56b69d61288072 --- test/functional/rpc_misc.py | 50 ++++++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 51 insertions(+) create mode 100755 test/functional/rpc_misc.py diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py new file mode 100755 index 000000000000..7bf8e68176b1 --- /dev/null +++ b/test/functional/rpc_misc.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# Copyright (c) 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. +"""Test RPC misc output.""" +import xml.etree.ElementTree as ET + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_raises_rpc_error, + assert_equal, + assert_greater_than, + assert_greater_than_or_equal, +) + +from test_framework.authproxy import JSONRPCException + + +class RpcMiscTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + node = self.nodes[0] + + self.log.info("test getmemoryinfo") + memory = node.getmemoryinfo()['locked'] + assert_greater_than(memory['used'], 0) + assert_greater_than(memory['free'], 0) + assert_greater_than(memory['total'], 0) + # assert_greater_than_or_equal() for locked in case locking pages failed at some point + assert_greater_than_or_equal(memory['locked'], 0) + assert_greater_than(memory['chunks_used'], 0) + assert_greater_than(memory['chunks_free'], 0) + assert_equal(memory['used'] + memory['free'], memory['total']) + + self.log.info("test mallocinfo") + try: + mallocinfo = node.getmemoryinfo(mode="mallocinfo") + self.log.info('getmemoryinfo(mode="mallocinfo") call succeeded') + tree = ET.fromstring(mallocinfo) + assert_equal(tree.tag, 'malloc') + except JSONRPCException: + self.log.info('getmemoryinfo(mode="mallocinfo") not available') + assert_raises_rpc_error(-8, 'mallocinfo is only available when compiled with glibc 2.10+', node.getmemoryinfo, mode="mallocinfo") + + assert_raises_rpc_error(-8, "unknown mode foobar", node.getmemoryinfo, mode="foobar") + +if __name__ == '__main__': + RpcMiscTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5211086f2a2f..24c8cce7d256 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -106,6 +106,7 @@ 'wallet_txn_doublespend.py --mineblock', 'wallet_txn_clone.py', 'rpc_getchaintips.py', + 'rpc_misc.py', 'interface_rest.py', 'mempool_spend_coinbase.py', 'mempool_reorg.py', From c3bd1dc6405c1b1edcadd570577adc2bb08989ad Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Sat, 23 Mar 2019 10:05:21 -0400 Subject: [PATCH 2/3] Merge #15647: [rpc] Remove deprecated functionality message from validateaddress help be8b9c62bf [rpc] Remove deprecated functionality message from validateaddress help (John Newbery) Pull request description: This functionality was removed in v0.18. ACKs for commit be8b9c: MarcoFalke: utACK be8b9c62bf3735d2ecb87bb39ba4816df8d2d3e0 fanquake: utACK be8b9c6 Tree-SHA512: 7e30f1e56020aa3a59e736574075191155866788156d0d30eaf963c0a5015ad0de43787c6b73a765319f2dccec0117a2230a312d18ca3f9be3986ce3de4afdb3 --- src/rpc/misc.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 650a6fc24c6f..f2af9ac13ae7 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -190,10 +190,6 @@ static UniValue validateaddress(const JSONRPCRequest& request) throw std::runtime_error( "validateaddress \"address\"\n" "\nReturn information about the given dash address.\n" - "DEPRECATION WARNING: Parts of this command have been deprecated and moved to getaddressinfo. Clients must\n" - "transition to using getaddressinfo to access this information before upgrading to v0.18. The following deprecated\n" - "fields have moved to getaddressinfo and will only be shown here with -deprecatedrpc=validateaddress: ismine, iswatchonly,\n" - "script, hex, pubkeys, sigsrequired, pubkey, addresses, embedded, iscompressed, account, timestamp, hdkeypath.\n" "\nArguments:\n" "1. \"address\" (string, required) The dash address to validate\n" "\nResult:\n" From 5167cbe911c3a666a5a7502c71990ee3a38c6bca Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Mon, 18 Feb 2019 10:55:34 +0100 Subject: [PATCH 3/3] Merge #15250: Use RdSeed when available, and reduce RdRand load 1435fabc19d2143187efb493cbe23225eaf851ae Use RdSeed when available, and reduce RdRand load (Pieter Wuille) Pull request description: This introduces support for autodetecting and using the RdSeed instruction on x86/x86_64 systems. In addition: * In SeedFast, only 64 bits of entropy are generated through RdRand (256 was relatively slow). * In SeedStartup, 256 bits of entropy are generated, using RdSeed (preferably) or RdRand (otherwise). Tree-SHA512: fb7d3e22e93e14592f4b07282aa79d7c3cc4e9debdd9978580b8d2562bbad345e289bf3f80de2c50c9b50b8bac2aa9b838f9f272f7f8d43f1efc0913aa8acce3 --- src/random.cpp | 178 +++++++++++++++++++++++++++++++++++++------------ src/random.h | 3 +- 2 files changed, 139 insertions(+), 42 deletions(-) diff --git a/src/random.cpp b/src/random.cpp index 9007eac2c800..d167573e1c73 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -78,25 +78,122 @@ static inline int64_t GetPerformanceCounter() noexcept } #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) -static bool rdrand_supported = false; +static bool g_rdrand_supported = false; +static bool g_rdseed_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; +static constexpr uint32_t CPUID_F7_EBX_RDSEED = 0x00040000; +#ifdef bit_RDRND +static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND"); +#endif +#ifdef bit_RDSEED +static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED"); +#endif +static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) +{ + // We can't use __get_cpuid as it doesn't support subleafs. +#ifdef __GNUC__ + __cpuid_count(leaf, subleaf, a, b, c, d); +#else + __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); +#endif +} + static void InitHardwareRand() { uint32_t eax, ebx, ecx, edx; - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx & CPUID_F1_ECX_RDRAND)) { - rdrand_supported = true; + GetCPUID(1, 0, eax, ebx, ecx, edx); + if (ecx & CPUID_F1_ECX_RDRAND) { + g_rdrand_supported = true; + } + GetCPUID(7, 0, eax, ebx, ecx, edx); + if (ebx & CPUID_F7_EBX_RDSEED) { + g_rdseed_supported = true; } } static void ReportHardwareRand() { - if (rdrand_supported) { - // This must be done in a separate function, as HWRandInit() may be indirectly called - // from global constructors, before logging is initialized. + // This must be done in a separate function, as HWRandInit() may be indirectly called + // from global constructors, before logging is initialized. + if (g_rdseed_supported) { + LogPrintf("Using RdSeed as additional entropy source\n"); + } + if (g_rdrand_supported) { LogPrintf("Using RdRand as an additional entropy source\n"); } } +/** Read 64 bits of entropy using rdrand. + * + * Must only be called when RdRand is supported. + */ +static uint64_t GetRdRand() noexcept +{ + // RdRand may very rarely fail. Invoke it up to 10 times in a loop to reduce this risk. +#ifdef __i386__ + uint8_t ok; + // Initialize to 0 to silence a compiler warning that r1 or r2 may be used + // uninitialized. Even if rdrand fails (!ok) it will set the output to 0, + // but there is no way that the compiler could know that. + uint32_t r1 = 0, r2 = 0; + for (int i = 0; i < 10; ++i) { + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %eax + if (ok) break; + } + for (int i = 0; i < 10; ++i) { + __asm__ volatile (".byte 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r2), "=q"(ok) :: "cc"); // rdrand %eax + if (ok) break; + } + return (((uint64_t)r2) << 32) | r1; +#elif defined(__x86_64__) || defined(__amd64__) + uint8_t ok; + uint64_t r1 = 0; // See above why we initialize to 0. + for (int i = 0; i < 10; ++i) { + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %rax + if (ok) break; + } + return r1; +#else +#error "RdRand is only supported on x86 and x86_64" +#endif +} + +/** Read 64 bits of entropy using rdseed. + * + * Must only be called when RdSeed is supported. + */ +static uint64_t GetRdSeed() noexcept +{ + // RdSeed may fail when the HW RNG is overloaded. Loop indefinitely until enough entropy is gathered, + // but pause after every failure. +#ifdef __i386__ + uint8_t ok; + uint32_t r1, r2; + do { + __asm__ volatile (".byte 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdseed %eax + if (ok) break; + __asm__ volatile ("pause"); + } while(true); + do { + __asm__ volatile (".byte 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r2), "=q"(ok) :: "cc"); // rdseed %eax + if (ok) break; + __asm__ volatile ("pause"); + } while(true); + return (((uint64_t)r2) << 32) | r1; +#elif defined(__x86_64__) || defined(__amd64__) + uint8_t ok; + uint64_t r1; + do { + __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf8; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdseed %rax + if (ok) break; + __asm__ volatile ("pause"); + } while(true); + return r1; +#else +#error "RdSeed is only supported on x86 and x86_64" +#endif +} + #else /* Access to other hardware random number generators could be added here later, * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). @@ -107,43 +204,40 @@ static void InitHardwareRand() {} static void ReportHardwareRand() {} #endif -static bool GetHardwareRand(unsigned char* ent32) noexcept { +/** Add 64 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */ +static void SeedHardwareFast(CSHA512& hasher) noexcept { #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) - if (rdrand_supported) { - uint8_t ok; - // Not all assemblers support the rdrand instruction, write it in hex. -#ifdef __i386__ - for (int iter = 0; iter < 4; ++iter) { - // Initialize to 0 to silence a compiler warning that r1 or r2 may be used - // uninitialized. Even if rdrand fails (!ok) it will set the output to 0, - // but there is no way that the compiler could know that. - uint32_t r1 = 0, r2 = 0; - __asm__ volatile (".byte 0x0f, 0xc7, 0xf0;" // rdrand %eax - ".byte 0x0f, 0xc7, 0xf2;" // rdrand %edx - "setc %2" : - "=a"(r1), "=d"(r2), "=q"(ok) :: "cc"); - if (!ok) return false; - WriteLE32(ent32 + 8 * iter, r1); - WriteLE32(ent32 + 8 * iter + 4, r2); - } -#else - uint64_t r1 = 0, r2 = 0, r3 = 0, r4 = 0; // See above why we initialize to 0. - __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0, " // rdrand %rax - "0x48, 0x0f, 0xc7, 0xf3, " // rdrand %rbx - "0x48, 0x0f, 0xc7, 0xf1, " // rdrand %rcx - "0x48, 0x0f, 0xc7, 0xf2; " // rdrand %rdx - "setc %4" : - "=a"(r1), "=b"(r2), "=c"(r3), "=d"(r4), "=q"(ok) :: "cc"); - if (!ok) return false; - WriteLE64(ent32, r1); - WriteLE64(ent32 + 8, r2); - WriteLE64(ent32 + 16, r3); - WriteLE64(ent32 + 24, r4); + if (g_rdrand_supported) { + uint64_t out = GetRdRand(); + hasher.Write((const unsigned char*)&out, sizeof(out)); + return; + } #endif - return true; +} + +/** Add 256 bits of entropy gathered from hardware to hasher. Do nothing if not supported. */ +static void SeedHardwareSlow(CSHA512& hasher) noexcept { +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) + // When we want 256 bits of entropy, prefer RdSeed over RdRand, as it's + // guaranteed to produce independent randomness on every call. + if (g_rdseed_supported) { + for (int i = 0; i < 4; ++i) { + uint64_t out = GetRdSeed(); + hasher.Write((const unsigned char*)&out, sizeof(out)); + } + return; + } + // When falling back to RdRand, XOR the result of 1024 results. + // This guarantees a reseeding occurs between each. + if (g_rdrand_supported) { + for (int i = 0; i < 4; ++i) { + uint64_t out = 0; + for (int j = 0; j < 1024; ++j) out ^= GetRdRand(); + hasher.Write((const unsigned char*)&out, sizeof(out)); + } + return; } #endif - return false; } /** Use repeated SHA512 to strengthen the randomness in seed32, and feed into hasher. */ @@ -431,8 +525,7 @@ static void SeedFast(CSHA512& hasher) noexcept hasher.Write((const unsigned char*)&ptr, sizeof(ptr)); // Hardware randomness is very fast when available; use it always. - bool have_hw_rand = GetHardwareRand(buffer); - if (have_hw_rand) hasher.Write(buffer, sizeof(buffer)); + SeedHardwareFast(hasher); // High-precision timestamp SeedTimestamp(hasher); @@ -503,6 +596,9 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept RAND_screen(); #endif + // Gather 256 bits of hardware randomness, if available + SeedHardwareSlow(hasher); + // Everything that the 'slow' seeder includes. SeedSlow(hasher); diff --git a/src/random.h b/src/random.h index 316aedc5978a..0923d791bfc9 100644 --- a/src/random.h +++ b/src/random.h @@ -25,7 +25,7 @@ * perform 'fast' seeding, consisting of mixing in: * - A stack pointer (indirectly committing to calling thread and call stack) * - A high-precision timestamp (rdtsc when available, c++ high_resolution_clock otherwise) - * - Hardware RNG (rdrand) when available. + * - 64 bits from the hardware RNG (rdrand) when available. * These entropy sources are very fast, and only designed to protect against situations * where a VM state restore/copy results in multiple systems with the same randomness. * FastRandomContext on the other hand does not protect against this once created, but @@ -50,6 +50,7 @@ * * On first use of the RNG (regardless of what function is called first), all entropy * sources used in the 'slow' seeder are included, but also: + * - 256 bits from the hardware RNG (rdseed or rdrand) when available. * - (On Windows) Performance monitoring data from the OS. * - (On Windows) Through OpenSSL, the screen contents. * - Strengthen the entropy for 100 ms using repeated SHA512.