Skip to content

Commit fed885f

Browse files
Permanent CVE-2017-12842 mitigation
1 parent ff497bf commit fed885f

File tree

12 files changed

+52
-23
lines changed

12 files changed

+52
-23
lines changed

src/consensus/tx_check.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <consensus/tx_check.h>
6+
#include <xep/consensus/tx_check.h>
67

78
#include <consensus/amount.h>
89
#include <primitives/transaction.h>
@@ -16,9 +17,13 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
1617
if (tx.vout.empty())
1718
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-vout-empty");
1819
// Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability)
19-
if (::GetSerializeSize(TX_NO_WITNESS(tx)) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) {
20+
const size_t size = ::GetSerializeSize(TX_NO_WITNESS(tx));
21+
if (size * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) {
2022
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-oversize");
2123
}
24+
if (!XEP_CheckTransactionSize(state, size)) {
25+
return false;
26+
}
2227

2328
// Check for negative or overflow output values (see CVE-2010-5139)
2429
CAmount nValueOut = 0;

src/policy/policy.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000};
3232
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000};
3333
/** The maximum weight for transactions we're willing to relay/mine */
3434
static constexpr int32_t MAX_STANDARD_TX_WEIGHT{400000};
35-
/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64 */
36-
static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{65};
3735
/** Maximum number of signature check operations in an IsStandard() P2SH script */
3836
static constexpr unsigned int MAX_P2SH_SIGOPS{15};
3937
/** The maximum number of sigops we're willing to relay/mine in a single tx */

src/test/data/tx_invalid.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@
149149
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "-1 CHECKLOCKTIMEVERIFY"]],
150150
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000065cd1d", "CHECKLOCKTIMEVERIFY"],
151151
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "1"]],
152-
"010000000100010000000000000000000000000000000000000000000000000000000000000000000004005194b1010000000100000000000000000002000000", "CHECKLOCKTIMEVERIFY"],
152+
"01000000010001000000000000000000000000000000000000000000000000000000000000000000000500005194b1010000000100000000000000000002000000", "CHECKLOCKTIMEVERIFY"],
153153

154154
["Input locked"],
155155
[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0 CHECKLOCKTIMEVERIFY 1"]],

src/validation.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -794,10 +794,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
794794
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
795795
}
796796

797-
// Transactions smaller than 65 non-witness bytes are not relayed to mitigate CVE-2017-12842.
798-
if (::GetSerializeSize(TX_NO_WITNESS(tx)) < MIN_STANDARD_TX_NONWITNESS_SIZE)
799-
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
800-
801797
// Only accept nLockTime-using transactions that can be mined in the next
802798
// block; we don't want our mempool filled up with transactions that can't
803799
// be mined yet.

src/wallet/spend.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal(
10271027
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
10281028
coin_selection_params.m_include_unsafe_inputs = coin_control.m_include_unsafe_inputs;
10291029
coin_selection_params.m_max_tx_weight = coin_control.m_max_tx_weight.value_or(MAX_STANDARD_TX_WEIGHT);
1030-
int minimum_tx_weight = MIN_STANDARD_TX_NONWITNESS_SIZE * WITNESS_SCALE_FACTOR;
1030+
int minimum_tx_weight = 0;
10311031
if (coin_selection_params.m_max_tx_weight.value() < minimum_tx_weight || coin_selection_params.m_max_tx_weight.value() > MAX_STANDARD_TX_WEIGHT) {
10321032
return util::Error{strprintf(_("Maximum transaction weight must be between %d and %d"), minimum_tx_weight, MAX_STANDARD_TX_WEIGHT)};
10331033
}

src/xep/consensus/tx_check.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2020-2025 The XEP 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+
#ifndef ELECTRAPROTOCOL_CONSENSUS_TX_CHECK_H
6+
#define ELECTRAPROTOCOL_CONSENSUS_TX_CHECK_H
7+
8+
#include <consensus/validation.h>
9+
10+
bool XEP_CheckTransactionSize(TxValidationState& state, size_t size)
11+
{
12+
// 64-byte transactions are rejected to mitigate CVE-2017-12842
13+
if (size == 64) {
14+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "tx-size-small");
15+
} else {
16+
return true;
17+
}
18+
};
19+
20+
#endif // ELECTRAPROTOCOL_CONSENSUS_TX_CHECK_H

test/functional/data/invalid_txs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ def get_tx(self):
117117
# tree depth commitment (CVE-2017-12842)
118118
class SizeTooSmall(BadTxTemplate):
119119
reject_reason = "tx-size-small"
120-
expect_disconnect = False
121-
valid_in_block = True
120+
expect_disconnect = True
121+
valid_in_block = False
122122

123123
def get_tx(self):
124124
tx = CTransaction()

test/functional/feature_block.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def run_test(self):
160160
blockname = f"for_invalid.{TxTemplate.__name__}"
161161
self.next_block(blockname)
162162
badtx = template.get_tx()
163-
if TxTemplate != invalid_txs.InputMissing:
163+
if TxTemplate != invalid_txs.InputMissing and TxTemplate != invalid_txs.SizeTooSmall:
164164
self.sign_tx(badtx, attempt_spend_tx)
165165
badtx.rehash()
166166
badblock = self.update_block(blockname, [badtx])

test/functional/p2p_ibd_stalling.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def run_test(self):
8282

8383
# Need to wait until 1023 blocks are received - the magic total bytes number is a workaround in lack of an rpc
8484
# returning the number of downloaded (but not connected) blocks.
85-
bytes_recv = 172761 if not self.options.v2transport else 169692
85+
bytes_recv = 174555 if not self.options.v2transport else 171486
8686
self.wait_until(lambda: self.total_bytes_recv_for_blocks() == bytes_recv)
8787

8888
self.all_sync_send_with_ping(peers)

test/functional/p2p_invalid_tx.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def run_test(self):
165165
node.p2ps[0].send_txs_and_test([rejected_parent], node, success=False)
166166

167167
self.log.info('Test that a peer disconnection causes erase its transactions from the orphan pool')
168-
with node.assert_debug_log(['Erased 100 orphan transaction(s) from peer=26']):
168+
with node.assert_debug_log(['Erased 100 orphan transaction(s) from peer=']):
169169
self.reconnect_p2p(num_connections=1)
170170

171171
self.log.info('Test that a transaction in the orphan pool is included in a new tip block causes erase this transaction from the orphan pool')

0 commit comments

Comments
 (0)