Skip to content

Commit f8723d2

Browse files
committed
Merge #8753: Locked memory manager
444c673 bench: Add benchmark for lockedpool allocation/deallocation (Wladimir J. van der Laan) 6567999 rpc: Add `getmemoryinfo` call (Wladimir J. van der Laan) 4536148 support: Add LockedPool (Wladimir J. van der Laan) f4d1fc2 wallet: Get rid of LockObject and UnlockObject calls in key.h (Wladimir J. van der Laan) 999e4c9 wallet: Change CCrypter to use vectors with secure allocator (Wladimir J. van der Laan)
2 parents 6a8be7b + 444c673 commit f8723d2

File tree

15 files changed

+966
-410
lines changed

15 files changed

+966
-410
lines changed

src/Makefile.am

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ BITCOIN_CORE_H = \
132132
support/allocators/secure.h \
133133
support/allocators/zeroafterfree.h \
134134
support/cleanse.h \
135-
support/pagelocker.h \
135+
support/lockedpool.h \
136136
sync.h \
137137
threadsafety.h \
138138
timedata.h \
@@ -310,7 +310,7 @@ libbitcoin_common_a_SOURCES = \
310310
libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
311311
libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
312312
libbitcoin_util_a_SOURCES = \
313-
support/pagelocker.cpp \
313+
support/lockedpool.cpp \
314314
chainparamsbase.cpp \
315315
clientversion.cpp \
316316
compat/glibc_sanity.cpp \

src/Makefile.bench.include

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ bench_bench_bitcoin_SOURCES = \
1717
bench/ccoins_caching.cpp \
1818
bench/mempool_eviction.cpp \
1919
bench/verify_script.cpp \
20-
bench/base58.cpp
20+
bench/base58.cpp \
21+
bench/lockedpool.cpp
2122

2223
bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/
2324
bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)

src/bench/lockedpool.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) 2016 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "bench.h"
6+
7+
#include "support/lockedpool.h"
8+
9+
#include <iostream>
10+
#include <vector>
11+
12+
#define ASIZE 2048
13+
#define BITER 5000
14+
#define MSIZE 2048
15+
16+
static void LockedPool(benchmark::State& state)
17+
{
18+
void *synth_base = reinterpret_cast<void*>(0x08000000);
19+
const size_t synth_size = 1024*1024;
20+
Arena b(synth_base, synth_size, 16);
21+
22+
std::vector<void*> addr;
23+
for (int x=0; x<ASIZE; ++x)
24+
addr.push_back(0);
25+
uint32_t s = 0x12345678;
26+
while (state.KeepRunning()) {
27+
for (int x=0; x<BITER; ++x) {
28+
int idx = s & (addr.size()-1);
29+
if (s & 0x80000000) {
30+
b.free(addr[idx]);
31+
addr[idx] = 0;
32+
} else if(!addr[idx]) {
33+
addr[idx] = b.alloc((s >> 16) & (MSIZE-1));
34+
}
35+
bool lsb = s & 1;
36+
s >>= 1;
37+
if (lsb)
38+
s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
39+
}
40+
}
41+
for (void *ptr: addr)
42+
b.free(ptr);
43+
addr.clear();
44+
}
45+
46+
BENCHMARK(LockedPool);
47+

src/key.cpp

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ bool CKey::Check(const unsigned char *vch) {
125125

126126
void CKey::MakeNewKey(bool fCompressedIn) {
127127
do {
128-
GetStrongRandBytes(vch, sizeof(vch));
129-
} while (!Check(vch));
128+
GetStrongRandBytes(keydata.data(), keydata.size());
129+
} while (!Check(keydata.data()));
130130
fValid = true;
131131
fCompressed = fCompressedIn;
132132
}
@@ -224,20 +224,18 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) {
224224
bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
225225
assert(IsValid());
226226
assert(IsCompressed());
227-
unsigned char out[64];
228-
LockObject(out);
227+
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
229228
if ((nChild >> 31) == 0) {
230229
CPubKey pubkey = GetPubKey();
231230
assert(pubkey.begin() + 33 == pubkey.end());
232-
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, out);
231+
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data());
233232
} else {
234233
assert(begin() + 32 == end());
235-
BIP32Hash(cc, nChild, 0, begin(), out);
234+
BIP32Hash(cc, nChild, 0, begin(), vout.data());
236235
}
237-
memcpy(ccChild.begin(), out+32, 32);
236+
memcpy(ccChild.begin(), vout.data()+32, 32);
238237
memcpy((unsigned char*)keyChild.begin(), begin(), 32);
239-
bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), out);
240-
UnlockObject(out);
238+
bool ret = secp256k1_ec_privkey_tweak_add(secp256k1_context_sign, (unsigned char*)keyChild.begin(), vout.data());
241239
keyChild.fCompressed = true;
242240
keyChild.fValid = ret;
243241
return ret;
@@ -253,12 +251,10 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
253251

254252
void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) {
255253
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
256-
unsigned char out[64];
257-
LockObject(out);
258-
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(out);
259-
key.Set(&out[0], &out[32], true);
260-
memcpy(chaincode.begin(), &out[32], 32);
261-
UnlockObject(out);
254+
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
255+
CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data());
256+
key.Set(&vout[0], &vout[32], true);
257+
memcpy(chaincode.begin(), &vout[32], 32);
262258
nDepth = 0;
263259
nChild = 0;
264260
memset(vchFingerprint, 0, sizeof(vchFingerprint));
@@ -308,12 +304,10 @@ void ECC_Start() {
308304

309305
{
310306
// Pass in a random blinding seed to the secp256k1 context.
311-
unsigned char seed[32];
312-
LockObject(seed);
313-
GetRandBytes(seed, 32);
314-
bool ret = secp256k1_context_randomize(ctx, seed);
307+
std::vector<unsigned char, secure_allocator<unsigned char>> vseed(32);
308+
GetRandBytes(vseed.data(), 32);
309+
bool ret = secp256k1_context_randomize(ctx, vseed.data());
315310
assert(ret);
316-
UnlockObject(seed);
317311
}
318312

319313
secp256k1_context_sign = ctx;

src/key.h

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,7 @@ class CKey
4343
bool fCompressed;
4444

4545
//! The actual byte data
46-
unsigned char vch[32];
47-
48-
static_assert(sizeof(vch) == 32, "vch must be 32 bytes in length to not break serialization");
46+
std::vector<unsigned char, secure_allocator<unsigned char> > keydata;
4947

5048
//! Check whether the 32-byte array pointed to be vch is valid keydata.
5149
bool static Check(const unsigned char* vch);
@@ -54,37 +52,30 @@ class CKey
5452
//! Construct an invalid private key.
5553
CKey() : fValid(false), fCompressed(false)
5654
{
57-
LockObject(vch);
58-
}
59-
60-
//! Copy constructor. This is necessary because of memlocking.
61-
CKey(const CKey& secret) : fValid(secret.fValid), fCompressed(secret.fCompressed)
62-
{
63-
LockObject(vch);
64-
memcpy(vch, secret.vch, sizeof(vch));
55+
// Important: vch must be 32 bytes in length to not break serialization
56+
keydata.resize(32);
6557
}
6658

6759
//! Destructor (again necessary because of memlocking).
6860
~CKey()
6961
{
70-
UnlockObject(vch);
7162
}
7263

7364
friend bool operator==(const CKey& a, const CKey& b)
7465
{
7566
return a.fCompressed == b.fCompressed &&
7667
a.size() == b.size() &&
77-
memcmp(&a.vch[0], &b.vch[0], a.size()) == 0;
68+
memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0;
7869
}
7970

8071
//! Initialize using begin and end iterators to byte data.
8172
template <typename T>
8273
void Set(const T pbegin, const T pend, bool fCompressedIn)
8374
{
84-
if (pend - pbegin != sizeof(vch)) {
75+
if (size_t(pend - pbegin) != keydata.size()) {
8576
fValid = false;
8677
} else if (Check(&pbegin[0])) {
87-
memcpy(vch, (unsigned char*)&pbegin[0], sizeof(vch));
78+
memcpy(keydata.data(), (unsigned char*)&pbegin[0], keydata.size());
8879
fValid = true;
8980
fCompressed = fCompressedIn;
9081
} else {
@@ -93,9 +84,9 @@ class CKey
9384
}
9485

9586
//! Simple read-only vector-like interface.
96-
unsigned int size() const { return (fValid ? sizeof(vch) : 0); }
97-
const unsigned char* begin() const { return vch; }
98-
const unsigned char* end() const { return vch + size(); }
87+
unsigned int size() const { return (fValid ? keydata.size() : 0); }
88+
const unsigned char* begin() const { return keydata.data(); }
89+
const unsigned char* end() const { return keydata.data() + size(); }
9990

10091
//! Check whether this private key is valid.
10192
bool IsValid() const { return fValid; }

src/rpc/misc.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,10 +450,53 @@ UniValue setmocktime(const JSONRPCRequest& request)
450450
return NullUniValue;
451451
}
452452

453+
static UniValue RPCLockedMemoryInfo()
454+
{
455+
LockedPool::Stats stats = LockedPoolManager::Instance().stats();
456+
UniValue obj(UniValue::VOBJ);
457+
obj.push_back(Pair("used", uint64_t(stats.used)));
458+
obj.push_back(Pair("free", uint64_t(stats.free)));
459+
obj.push_back(Pair("total", uint64_t(stats.total)));
460+
obj.push_back(Pair("locked", uint64_t(stats.locked)));
461+
obj.push_back(Pair("chunks_used", uint64_t(stats.chunks_used)));
462+
obj.push_back(Pair("chunks_free", uint64_t(stats.chunks_free)));
463+
return obj;
464+
}
465+
466+
UniValue getmemoryinfo(const JSONRPCRequest& request)
467+
{
468+
/* Please, avoid using the word "pool" here in the RPC interface or help,
469+
* as users will undoubtedly confuse it with the other "memory pool"
470+
*/
471+
if (request.fHelp || request.params.size() != 0)
472+
throw runtime_error(
473+
"getmemoryinfo\n"
474+
"Returns an object containing information about memory usage.\n"
475+
"\nResult:\n"
476+
"{\n"
477+
" \"locked\": { (json object) Information about locked memory manager\n"
478+
" \"used\": xxxxx, (numeric) Number of bytes used\n"
479+
" \"free\": xxxxx, (numeric) Number of bytes available in current arenas\n"
480+
" \"total\": xxxxxxx, (numeric) Total number of bytes managed\n"
481+
" \"locked\": xxxxxx, (numeric) Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk.\n"
482+
" \"chunks_used\": xxxxx, (numeric) Number allocated chunks\n"
483+
" \"chunks_free\": xxxxx, (numeric) Number unused chunks\n"
484+
" }\n"
485+
"}\n"
486+
"\nExamples:\n"
487+
+ HelpExampleCli("getmemoryinfo", "")
488+
+ HelpExampleRpc("getmemoryinfo", "")
489+
);
490+
UniValue obj(UniValue::VOBJ);
491+
obj.push_back(Pair("locked", RPCLockedMemoryInfo()));
492+
return obj;
493+
}
494+
453495
static const CRPCCommand commands[] =
454496
{ // category name actor (function) okSafeMode
455497
// --------------------- ------------------------ ----------------------- ----------
456498
{ "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */
499+
{ "control", "getmemoryinfo", &getmemoryinfo, true },
457500
{ "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */
458501
{ "util", "createmultisig", &createmultisig, true },
459502
{ "util", "verifymessage", &verifymessage, true },

src/support/allocators/secure.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#ifndef BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
77
#define BITCOIN_SUPPORT_ALLOCATORS_SECURE_H
88

9-
#include "support/pagelocker.h"
9+
#include "support/lockedpool.h"
10+
#include "support/cleanse.h"
1011

1112
#include <string>
1213

@@ -39,20 +40,15 @@ struct secure_allocator : public std::allocator<T> {
3940

4041
T* allocate(std::size_t n, const void* hint = 0)
4142
{
42-
T* p;
43-
p = std::allocator<T>::allocate(n, hint);
44-
if (p != NULL)
45-
LockedPageManager::Instance().LockRange(p, sizeof(T) * n);
46-
return p;
43+
return static_cast<T*>(LockedPoolManager::Instance().alloc(sizeof(T) * n));
4744
}
4845

4946
void deallocate(T* p, std::size_t n)
5047
{
5148
if (p != NULL) {
5249
memory_cleanse(p, sizeof(T) * n);
53-
LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n);
5450
}
55-
std::allocator<T>::deallocate(p, n);
51+
LockedPoolManager::Instance().free(p);
5652
}
5753
};
5854

0 commit comments

Comments
 (0)