Skip to content

Commit feb162d

Browse files
committed
Merge #14696: qa: Add explicit references to related CVE's in p2p_invalid_block test.
0c62e3a New regression testing for CVE-2018-17144, CVE-2012-2459, and CVE-2010-5137. (lucash-dev) 38bfca6 Added comments referencing multiple CVEs in tests and production code. (lucash-dev) Pull request description: This functional test includes two scenarios that test for regressions of vulnerabilities, but they are only briefly described. There are freely available documents explaining in detail the issues, but without explicit mentions, the developer trying to maintain the code needs an additional step of digging in commit history and PR conversations to figure it out. Added comments to explicitly mention CVE-2018-17144 and CVE-2012-2459, for more complete documentation. This improves developer experience by making understanding the tests easier. ACKs for top commit: laanwj: ACK 0c62e3a, checked the CVE numbers, thanks for adding documentation Tree-SHA512: 3ee05351745193b8b959e4a25d50f25a693b2d24b0732ed53cf7d5882df40b5dd0f1877bd5c69cffb921d4a7acf9deb3cc1160b96dc730d9b5984151ad06b7c9
2 parents 4b5e5ef + 0c62e3a commit feb162d

File tree

9 files changed

+104
-17
lines changed

9 files changed

+104
-17
lines changed

src/consensus/tx_check.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
1818
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT)
1919
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-txns-oversize");
2020

21-
// Check for negative or overflow output values
21+
// Check for negative or overflow output values (see CVE-2010-5139)
2222
CAmount nValueOut = 0;
2323
for (const auto& txout : tx.vout)
2424
{

src/net_processing.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2559,7 +2559,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
25592559
}
25602560
AddOrphanTx(ptx, pfrom->GetId());
25612561

2562-
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
2562+
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded (see CVE-2012-3789)
25632563
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, gArgs.GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
25642564
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
25652565
if (nEvicted > 0) {

src/script/interpreter.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
334334
opcode == OP_MOD ||
335335
opcode == OP_LSHIFT ||
336336
opcode == OP_RSHIFT)
337-
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes.
337+
return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes (CVE-2010-5137).
338338

339339
// With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch
340340
if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE))
@@ -1483,6 +1483,8 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C
14831483
return set_error(serror, SCRIPT_ERR_SIG_PUSHONLY);
14841484
}
14851485

1486+
// scriptSig and scriptPubKey must be evaluated sequentially on the same stack
1487+
// rather than being simply concatenated (see CVE-2010-5141)
14861488
std::vector<std::vector<unsigned char> > stack, stackCopy;
14871489
if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror))
14881490
// serror is set

src/test/data/script_tests.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,15 +829,16 @@
829829
["NOP", "2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
830830
["1", "2 3 2SWAP 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
831831

832+
833+
["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
834+
835+
["TEST DISABLED OP CODES (CVE-2010-5137)"],
832836
["'a' 'b'", "CAT", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
833837
["'a' 'b' 0", "IF CAT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "CAT disabled"],
834838
["'abc' 1 1", "SUBSTR", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
835839
["'abc' 1 1 0", "IF SUBSTR ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "SUBSTR disabled"],
836840
["'abc' 2 0", "IF LEFT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "LEFT disabled"],
837841
["'abc' 2 0", "IF RIGHT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "RIGHT disabled"],
838-
839-
["NOP", "SIZE 1", "P2SH,STRICTENC", "INVALID_STACK_OPERATION"],
840-
841842
["'abc'", "IF INVERT ELSE 1 ENDIF", "P2SH,STRICTENC", "DISABLED_OPCODE", "INVERT disabled"],
842843
["1 2 0 IF AND ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "AND disabled"],
843844
["1 2 0 IF OR ELSE 1 ENDIF", "NOP", "P2SH,STRICTENC", "DISABLED_OPCODE", "OR disabled"],

src/validation.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1828,7 +1828,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
18281828
// If such overwrites are allowed, coinbases and transactions depending upon those
18291829
// can be duplicated to remove the ability to spend the first instance -- even after
18301830
// being sent to another address.
1831-
// See BIP30 and http://r6.ca/blog/20120206T005236Z.html for more information.
1831+
// See BIP30, CVE-2012-1909, and http://r6.ca/blog/20120206T005236Z.html for more information.
18321832
// This logic is not necessary for memory pool transactions, as AcceptToMemoryPool
18331833
// already refuses previously-known transaction ids entirely.
18341834
// This rule was originally applied to all blocks with a timestamp after March 15, 2012, 0:00 UTC.
@@ -3100,6 +3100,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P
31003100
return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-cb-multiple", "more than one coinbase");
31013101

31023102
// Check transactions
3103+
// Must check for duplicate inputs (see CVE-2018-17144)
31033104
for (const auto& tx : block.vtx)
31043105
if (!CheckTransaction(*tx, state, true))
31053106
return state.Invalid(state.GetReason(), false, state.GetRejectCode(), state.GetRejectReason(),

test/functional/data/invalid_txs.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,24 @@
2424
from test_framework.messages import CTransaction, CTxIn, CTxOut, COutPoint
2525
from test_framework import script as sc
2626
from test_framework.blocktools import create_tx_with_script, MAX_BLOCK_SIGOPS
27-
27+
from test_framework.script import (
28+
CScript,
29+
OP_CAT,
30+
OP_SUBSTR,
31+
OP_LEFT,
32+
OP_RIGHT,
33+
OP_INVERT,
34+
OP_AND,
35+
OP_OR,
36+
OP_XOR,
37+
OP_2MUL,
38+
OP_2DIV,
39+
OP_MUL,
40+
OP_DIV,
41+
OP_MOD,
42+
OP_LSHIFT,
43+
OP_RSHIFT
44+
)
2845
basic_p2sh = sc.CScript([sc.OP_HASH160, sc.hash160(sc.CScript([sc.OP_0])), sc.OP_EQUAL])
2946

3047

@@ -178,7 +195,44 @@ def get_tx(self):
178195
script_pub_key=lotsa_checksigs,
179196
amount=1)
180197

198+
def getDisabledOpcodeTemplate(opcode):
199+
""" Creates disabled opcode tx template class"""
200+
def get_tx(self):
201+
tx = CTransaction()
202+
vin = self.valid_txin
203+
vin.scriptSig = CScript([opcode])
204+
tx.vin.append(vin)
205+
tx.vout.append(CTxOut(1, basic_p2sh))
206+
tx.calc_sha256()
207+
return tx
208+
209+
return type('DisabledOpcode_' + str(opcode), (BadTxTemplate,), {
210+
'reject_reason': "disabled opcode",
211+
'expect_disconnect': True,
212+
'get_tx': get_tx,
213+
'valid_in_block' : True
214+
})
215+
216+
# Disabled opcode tx templates (CVE-2010-5137)
217+
DisabledOpcodeTemplates = [getDisabledOpcodeTemplate(opcode) for opcode in [
218+
OP_CAT,
219+
OP_SUBSTR,
220+
OP_LEFT,
221+
OP_RIGHT,
222+
OP_INVERT,
223+
OP_AND,
224+
OP_OR,
225+
OP_XOR,
226+
OP_2MUL,
227+
OP_2DIV,
228+
OP_MUL,
229+
OP_DIV,
230+
OP_MOD,
231+
OP_LSHIFT,
232+
OP_RSHIFT]]
233+
181234

182235
def iter_all_templates():
183236
"""Iterate through all bad transaction template types."""
184237
return BadTxTemplate.__subclasses__()
238+

test/functional/feature_block.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ def run_test(self):
806806
#
807807
# Blocks are not allowed to contain a transaction whose id matches that of an earlier,
808808
# not-fully-spent transaction in the same chain. To test, make identical coinbases;
809-
# the second one should be rejected.
809+
# the second one should be rejected. See also CVE-2012-1909.
810810
#
811811
self.log.info("Reject a block with a transaction with a duplicate hash of a previous transaction (BIP30)")
812812
self.move_tip(60)

test/functional/mempool_accept.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def run_test(self):
211211
rawtxs=[tx.serialize().hex()],
212212
)
213213

214+
# The following two validations prevent overflow of the output amounts (see CVE-2010-5139).
214215
self.log.info('A transaction with too large output value')
215216
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference)))
216217
tx.vout[0].nValue = 21000000 * COIN + 1

test/functional/p2p_invalid_block.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,11 @@ def run_test(self):
5353
block_time = best_block["time"] + 1
5454

5555
# Use merkle-root malleability to generate an invalid block with
56-
# same blockheader.
56+
# same blockheader (CVE-2012-2459).
5757
# Manufacture a block with 3 transactions (coinbase, spend of prior
5858
# coinbase, spend of that spend). Duplicate the 3rd transaction to
5959
# leave merkle root and blockheader unchanged but invalidate the block.
60+
# For more information on merkle-root malleability see src/consensus/merkle.cpp.
6061
self.log.info("Test merkle root malleability.")
6162

6263
block2 = create_block(tip, create_coinbase(height), block_time)
@@ -81,15 +82,16 @@ def run_test(self):
8182

8283
node.p2p.send_blocks_and_test([block2], node, success=False, reject_reason='bad-txns-duplicate')
8384

84-
# Check transactions for duplicate inputs
85+
# Check transactions for duplicate inputs (CVE-2018-17144)
8586
self.log.info("Test duplicate input block.")
8687

87-
block2_orig.vtx[2].vin.append(block2_orig.vtx[2].vin[0])
88-
block2_orig.vtx[2].rehash()
89-
block2_orig.hashMerkleRoot = block2_orig.calc_merkle_root()
90-
block2_orig.rehash()
91-
block2_orig.solve()
92-
node.p2p.send_blocks_and_test([block2_orig], node, success=False, reject_reason='bad-txns-inputs-duplicate')
88+
block2_dup = copy.deepcopy(block2_orig)
89+
block2_dup.vtx[2].vin.append(block2_dup.vtx[2].vin[0])
90+
block2_dup.vtx[2].rehash()
91+
block2_dup.hashMerkleRoot = block2_dup.calc_merkle_root()
92+
block2_dup.rehash()
93+
block2_dup.solve()
94+
node.p2p.send_blocks_and_test([block2_dup], node, success=False, reject_reason='bad-txns-inputs-duplicate')
9395

9496
self.log.info("Test very broken block.")
9597

@@ -105,5 +107,31 @@ def run_test(self):
105107
node.p2p.send_blocks_and_test([block3], node, success=False, reject_reason='bad-cb-amount')
106108

107109

110+
# Complete testing of CVE-2012-2459 by sending the original block.
111+
# It should be accepted even though it has the same hash as the mutated one.
112+
113+
self.log.info("Test accepting original block after rejecting its mutated version.")
114+
node.p2p.send_blocks_and_test([block2_orig], node, success=True, timeout=5)
115+
116+
# Update tip info
117+
height += 1
118+
block_time += 1
119+
tip = int(block2_orig.hash, 16)
120+
121+
# Complete testing of CVE-2018-17144, by checking for the inflation bug.
122+
# Create a block that spends the output of a tx in a previous block.
123+
block4 = create_block(tip, create_coinbase(height), block_time)
124+
tx3 = create_tx_with_script(tx2, 0, script_sig=b'\x51', amount=50 * COIN)
125+
126+
# Duplicates input
127+
tx3.vin.append(tx3.vin[0])
128+
tx3.rehash()
129+
block4.vtx.append(tx3)
130+
block4.hashMerkleRoot = block4.calc_merkle_root()
131+
block4.rehash()
132+
block4.solve()
133+
self.log.info("Test inflation by duplicating input")
134+
node.p2p.send_blocks_and_test([block4], node, success=False, reject_reason='bad-txns-inputs-duplicate')
135+
108136
if __name__ == '__main__':
109137
InvalidBlockRequestTest().main()

0 commit comments

Comments
 (0)