The signature cache is set up here:
// To be called once in AppInit2/TestingSetup to initialize the signatureCache
void InitSignatureCache()
{
size_t nMaxCacheSize = GetArg("-maxsigcachesize", DEFAULT_MAX_SIG_CACHE_SIZE) * ((size_t) 1 << 20);
if (nMaxCacheSize <= 0) return;
size_t nElems = signatureCache.setup_bytes(nMaxCacheSize);
LogPrintf("Using %zu MiB out of %zu requested for signature cache, able to store %zu elements\n",
(nElems*sizeof(uint256)) >>20, nMaxCacheSize>>20, nElems);
}
if -maxsigcachesize is set to zero, then we just return from the function back into AppInit2. Note that we don't call signatureCache.setup_bytes().
We later try to use the sigcache and segfault.
Here's a comment from the CuckcooCache::cache class:
/** You must always construct a cache with some elements via a subsequent
* call to setup or setup_bytes, otherwise operations may segfault.
*/
and indeed we do:
(gdb) bt
[#0](/bitcoin-bitcoin/0/) __memcmp_sse4_1 () at ../sysdeps/x86_64/multiarch/memcmp-sse4.S:1525
[#1](/bitcoin-bitcoin/1/) 0x000056317c16411b in base_blob<256u>::Compare (other=..., this=0x0) at ./uint256.h:45
[#2](/bitcoin-bitcoin/2/) operator== (b=..., a=...) at ./uint256.h:47
[#3](/bitcoin-bitcoin/3/) CuckooCache::cache<uint256, (anonymous namespace)::SignatureCacheHasher>::contains (this=0x56317c767100 <(anonymous namespace)::signatureCache+32>, erase=254, e=...) at ./cuckoocache.h:447
[#4](/bitcoin-bitcoin/4/) (anonymous namespace)::CSignatureCache::Get (this=0x56317c7670e0 <(anonymous namespace)::signatureCache>, erase=254, entry=...) at script/sigcache.cpp:70
[#5](/bitcoin-bitcoin/5/) CachingTransactionSignatureChecker::VerifySignature (this=0x7f1d077fae00, vchSig=std::vector of length 71, capacity 72 = {...}, pubkey=..., sighash=...) at script/sigcache.cpp:107
[#6](/bitcoin-bitcoin/6/) 0x000056317c34e4a2 in TransactionSignatureChecker::CheckSig (this=0x7f1d077fae00, vchSigIn=std::vector of length 72, capacity 72 = {...}, vchPubKey=..., scriptCode=..., sigversion=SIGVERSION_BASE) at script/interpreter.cpp:1268
[#7](/bitcoin-bitcoin/7/) 0x000056317c347143 in EvalScript (stack=std::vector of length 2, capacity 2 = {...}, script=..., flags=flags@entry=65503, checker=..., sigversion=sigversion@entry=SIGVERSION_BASE, serror=serror@entry=0x7f1d077faf3c) at script/interpreter.cpp:902
[#8](/bitcoin-bitcoin/8/) 0x000056317c34f2cb in VerifyScript (scriptSig=..., scriptPubKey=..., witness=0x7f1cd4000ff8, flags=65503, checker=..., serror=serror@entry=0x7f1d077faf3c) at script/interpreter.cpp:1429
[#9](/bitcoin-bitcoin/9/) 0x000056317c1a1653 in CScriptCheck::operator() (this=this@entry=0x7f1d077faf00) at validation.cpp:1359
[#10](/bitcoin-bitcoin/10/) 0x000056317c1a6073 in CheckInputs (tx=..., state=..., inputs=..., fScriptChecks=fScriptChecks@entry=true, flags=flags@entry=65503, cacheStore=cacheStore@entry=true, txdata=..., pvChecks=0x0) at validation.cpp:1448
[#11](/bitcoin-bitcoin/11/) 0x000056317c1b8186 in AcceptToMemoryPoolWorker (pool=..., state=..., ptx=std::shared_ptr (count 3, weak 0) 0x7f1cd4000bf0, fLimitFree=fLimitFree@entry=true, pfMissingInputs=pfMissingInputs@entry=0x0, nAcceptTime=<optimized out>, plTxnReplaced=0x0,
fOverrideMempoolLimit=false, nAbsurdFee=@0x7f1d077fc508: 10000000, vHashTxnToUncache=std::vector of length 0, capacity 0) at validation.cpp:953
[#12](/bitcoin-bitcoin/12/) 0x000056317c1b9a1b in AcceptToMemoryPoolWithTime (pool=..., state=..., tx=std::shared_ptr (count 3, weak 0) 0x7f1cd4000bf0, fLimitFree=fLimitFree@entry=true, pfMissingInputs=pfMissingInputs@entry=0x0, nAcceptTime=<optimized out>, plTxnReplaced=0x0,
fOverrideMempoolLimit=false, nAbsurdFee=10000000) at validation.cpp:1021
[#13](/bitcoin-bitcoin/13/) 0x000056317c1b9b89 in AcceptToMemoryPool (pool=..., state=..., tx=std::shared_ptr (count 3, weak 0) 0x7f1cd4000bf0, fLimitFree=fLimitFree@entry=true, pfMissingInputs=pfMissingInputs@entry=0x0, plTxnReplaced=plTxnReplaced@entry=0x0, fOverrideMempoolLimit=false,
nAbsurdFee=10000000) at validation.cpp:1036
[#14](/bitcoin-bitcoin/14/) 0x000056317c2ef51a in CMerkleTx::AcceptToMemoryPool (state=..., nAbsurdFee=<optimized out>, this=0x7f1d077fd0a0) at wallet/wallet.cpp:3950
[#15](/bitcoin-bitcoin/15/) CWallet::CommitTransaction (this=0x56317cc8b740, wtxNew=..., reservekey=..., connman=0x56317caee7e0, state=...) at wallet/wallet.cpp:2751
[#16](/bitcoin-bitcoin/16/) 0x000056317c2a706e in SendMoney (address=..., nValue=nValue@entry=100000000, fSubtractFeeFromAmount=fSubtractFeeFromAmount@entry=false, wtxNew=...) at wallet/rpcwallet.cpp:372
[#17](/bitcoin-bitcoin/17/) 0x000056317c2b2871 in sendtoaddress (request=...) at wallet/rpcwallet.cpp:431
[#18](/bitcoin-bitcoin/18/) 0x000056317c15a33b in CRPCTable::execute (this=<optimized out>, request=...) at rpc/server.cpp:492
[#19](/bitcoin-bitcoin/19/) 0x000056317c2184b7 in HTTPReq_JSONRPC (req=0x7f1ce4001620) at httprpc.cpp:193
[#20](/bitcoin-bitcoin/20/) 0x000056317c21c002 in std::function<bool (HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>::operator()(HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const (
__args#1="", __args#0=0x7f1ce4001620, this=0x7f1ce40016b0) at /usr/include/c++/5/functional:2267
[#21](/bitcoin-bitcoin/21/) HTTPWorkItem::operator() (this=0x7f1ce4001680) at httpserver.cpp:51
[#22](/bitcoin-bitcoin/22/) WorkQueue<HTTPClosure>::Run (this=0x56317ca410d0) at httpserver.cpp:132
[#23](/bitcoin-bitcoin/23/) HTTPWorkQueueRun (queue=0x56317ca410d0) at httpserver.cpp:361
[#24](/bitcoin-bitcoin/24/) 0x00007f1d0eabdc80 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
[#25](/bitcoin-bitcoin/25/) 0x00007f1d0e2d06ba in start_thread (arg=0x7f1d077fe700) at pthread_create.c:333
[#26](/bitcoin-bitcoin/26/) 0x00007f1d0e00682d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
Trivially reproducible for me.
I believe the fix is to add a minimum signature cache size constant MIN_SIG_CACHE_SIZE and change this:
if (nMaxCacheSize <= 0) return;
to this:
if (nMaxCacheSize <= MIN_SIG_CACHE_SIZE) {
nMaxCacheSize = MIN_SIG_CACHE_SIZE
LogPrintf("maxsigcachesize must be at least %d. Setting maxsigcachesize to %d", MIN_SIG_CACHE_SIZE, MIN_SIG_CACHE_SIZE);
InitWarning(strprintf(_(maxsigcachesize must be at least %d. Setting maxsigcachesize to %d", MIN_SIG_CACHE_SIZE, MIN_SIG_CACHE_SIZE));
}
in InitSignatureCache()