mirror of
https://github.com/wownero/wownero.git
synced 2026-10-22 01:48:34 +02:00
cryptonote_basic: parse_and_validate_tx param to check max size
Simple API to check max blob size before parsing. This is useful when reading blobs from untrusted sources. @selsta pointed out that coinbase txs can technically be larger than get_max_tx_size(), otherwise we could enforce it on all txs.
This commit is contained in:
@@ -120,6 +120,13 @@ namespace cryptonote
|
|||||||
}
|
}
|
||||||
return total_key_offsets >= max_allowed;
|
return total_key_offsets >= max_allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool passes_max_size_check(const bool max_size_check, const blobdata_ref &tx_blob)
|
||||||
|
{
|
||||||
|
if (!max_size_check)
|
||||||
|
return true;
|
||||||
|
return tx_blob.size() <= get_max_tx_size();
|
||||||
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +226,9 @@ namespace cryptonote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx)
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||||
bool r = ::serialization::serialize(ba, tx);
|
bool r = ::serialization::serialize(ba, tx);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||||
@@ -231,8 +239,9 @@ namespace cryptonote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx)
|
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||||
bool r = tx.serialize_base(ba);
|
bool r = tx.serialize_base(ba);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||||
@@ -242,8 +251,9 @@ namespace cryptonote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx)
|
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx, const bool max_size_check)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||||
bool r = ::serialization::serialize_noeof(ba, tx);
|
bool r = ::serialization::serialize_noeof(ba, tx);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction prefix from blob");
|
||||||
@@ -251,8 +261,9 @@ namespace cryptonote
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash)
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, const bool max_size_check)
|
||||||
{
|
{
|
||||||
|
CHECK_AND_ASSERT_MES(passes_max_size_check(max_size_check, tx_blob), false, "Tx blob too big");
|
||||||
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
binary_archive<false> ba{epee::strspan<std::uint8_t>(tx_blob)};
|
||||||
bool r = ::serialization::serialize(ba, tx);
|
bool r = ::serialization::serialize(ba, tx);
|
||||||
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob");
|
||||||
@@ -265,9 +276,9 @@ namespace cryptonote
|
|||||||
return get_transaction_hash(tx, tx_hash);
|
return get_transaction_hash(tx, tx_hash);
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------------
|
//---------------------------------------------------------------
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash)
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const bool max_size_check)
|
||||||
{
|
{
|
||||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash))
|
if (!parse_and_validate_tx_from_blob(tx_blob, tx, tx_hash, max_size_check))
|
||||||
return false;
|
return false;
|
||||||
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -52,11 +52,12 @@ namespace cryptonote
|
|||||||
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev);
|
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx, hw::device &hwdev);
|
||||||
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
|
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
|
||||||
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
|
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
|
||||||
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx);
|
/* The size check is useful before parsing non-coinbase txs from untrusted sources. Coinbase blob sizes may be uncapped. */
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash);
|
bool parse_and_validate_tx_prefix_from_blob(const blobdata_ref& tx_blob, transaction_prefix& tx, const bool max_size_check = false);
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash);
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, crypto::hash& tx_prefix_hash, const bool max_size_check = false);
|
||||||
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx);
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, crypto::hash& tx_hash, const bool max_size_check = false);
|
||||||
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx);
|
bool parse_and_validate_tx_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check = false);
|
||||||
|
bool parse_and_validate_tx_base_from_blob(const blobdata_ref& tx_blob, transaction& tx, const bool max_size_check = false);
|
||||||
bool is_v1_tx(const blobdata_ref& tx_blob);
|
bool is_v1_tx(const blobdata_ref& tx_blob);
|
||||||
bool is_v1_tx(const blobdata& tx_blob);
|
bool is_v1_tx(const blobdata& tx_blob);
|
||||||
|
|
||||||
|
|||||||
@@ -5060,7 +5060,7 @@ bool Blockchain::prepare_handle_incoming_blocks(const std::vector<block_complete
|
|||||||
crypto::hash &tx_prefix_hash = txes[tx_index].second;
|
crypto::hash &tx_prefix_hash = txes[tx_index].second;
|
||||||
++tx_index;
|
++tx_index;
|
||||||
|
|
||||||
if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx))
|
if (!parse_and_validate_tx_base_from_blob(tx_blob.blob, tx, true))
|
||||||
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
|
SCAN_TABLE_QUIT("Could not parse tx from incoming blocks.");
|
||||||
cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
|
cryptonote::get_transaction_prefix_hash(tx, tx_prefix_hash);
|
||||||
|
|
||||||
|
|||||||
@@ -784,7 +784,7 @@ namespace cryptonote
|
|||||||
|
|
||||||
transaction tx;
|
transaction tx;
|
||||||
crypto::hash txid;
|
crypto::hash txid;
|
||||||
if (!parse_and_validate_tx_from_blob(tx_blob, tx, txid))
|
if (!parse_and_validate_tx_from_blob(tx_blob, tx, txid, true))
|
||||||
{
|
{
|
||||||
LOG_PRINT_L1("Incoming transactions failed to parse, rejected");
|
LOG_PRINT_L1("Incoming transactions failed to parse, rejected");
|
||||||
tvc.m_verifivation_failed = true;
|
tvc.m_verifivation_failed = true;
|
||||||
@@ -1197,7 +1197,7 @@ namespace cryptonote
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < tx_blobs.size(); ++i)
|
for (std::size_t i = 0; i < tx_blobs.size(); ++i)
|
||||||
{
|
{
|
||||||
if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i]))
|
if (!parse_and_validate_tx_from_blob(tx_blobs[i], txs[i], tx_hashes[i], true))
|
||||||
{
|
{
|
||||||
LOG_ERROR("Failed to parse relayed transaction");
|
LOG_ERROR("Failed to parse relayed transaction");
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ bool tx_sanity_check(const cryptonote::blobdata &tx_blob, uint64_t rct_outs_avai
|
|||||||
{
|
{
|
||||||
cryptonote::transaction tx;
|
cryptonote::transaction tx;
|
||||||
|
|
||||||
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx))
|
if (!cryptonote::parse_and_validate_tx_from_blob(tx_blob, tx, true))
|
||||||
{
|
{
|
||||||
MERROR("Failed to parse transaction");
|
MERROR("Failed to parse transaction");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -98,12 +98,6 @@ namespace cryptonote
|
|||||||
|
|
||||||
for (const cryptonote::tx_blob_entry& tx_entry: tx_entries)
|
for (const cryptonote::tx_blob_entry& tx_entry: tx_entries)
|
||||||
{
|
{
|
||||||
if (tx_entry.blob.size() > get_max_tx_size())
|
|
||||||
{
|
|
||||||
MERROR("Transaction blob of length " << tx_entry.blob.size() << " is too large to unpack!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool is_pruned = tx_entry.prunable_hash != crypto::null_hash;
|
const bool is_pruned = tx_entry.prunable_hash != crypto::null_hash;
|
||||||
if (is_pruned && !allow_pruned)
|
if (is_pruned && !allow_pruned)
|
||||||
{
|
{
|
||||||
@@ -114,14 +108,15 @@ namespace cryptonote
|
|||||||
cryptonote::transaction tx;
|
cryptonote::transaction tx;
|
||||||
crypto::hash tx_hash;
|
crypto::hash tx_hash;
|
||||||
bool parse_success = false;
|
bool parse_success = false;
|
||||||
|
const bool max_size_check = true;
|
||||||
if (is_pruned)
|
if (is_pruned)
|
||||||
{
|
{
|
||||||
if ((parse_success = cryptonote::parse_and_validate_tx_base_from_blob(tx_entry.blob, tx)))
|
if ((parse_success = cryptonote::parse_and_validate_tx_base_from_blob(tx_entry.blob, tx, max_size_check)))
|
||||||
parse_success = cryptonote::get_pruned_transaction_hash(tx, tx_entry.prunable_hash, tx_hash);
|
parse_success = cryptonote::get_pruned_transaction_hash(tx, tx_entry.prunable_hash, tx_hash);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
parse_success = cryptonote::parse_and_validate_tx_from_blob(tx_entry.blob, tx, tx_hash);
|
parse_success = cryptonote::parse_and_validate_tx_from_blob(tx_entry.blob, tx, tx_hash, max_size_check);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parse_success)
|
if (!parse_success)
|
||||||
@@ -931,7 +926,7 @@ namespace cryptonote
|
|||||||
|
|
||||||
for (const auto &blob: arg.txs)
|
for (const auto &blob: arg.txs)
|
||||||
{
|
{
|
||||||
MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash);, ret, "Including transaction " << hash);
|
MLOGIF_P2P_MESSAGE(cryptonote::transaction tx; crypto::hash hash; bool ret = cryptonote::parse_and_validate_tx_from_blob(blob, tx, hash, true);, ret, "Including transaction " << hash);
|
||||||
|
|
||||||
crypto::hash digest{};
|
crypto::hash digest{};
|
||||||
if (!blob.empty())
|
if (!blob.empty())
|
||||||
|
|||||||
@@ -3152,7 +3152,7 @@ void wallet2::process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_
|
|||||||
for (const auto &pool_tx: res.added_pool_txs)
|
for (const auto &pool_tx: res.added_pool_txs)
|
||||||
{
|
{
|
||||||
cryptonote::transaction tx;
|
cryptonote::transaction tx;
|
||||||
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_base_from_blob(pool_tx.tx_blob, tx),
|
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_base_from_blob(pool_tx.tx_blob, tx, true),
|
||||||
error::wallet_internal_error, "Failed to validate transaction base from daemon");
|
error::wallet_internal_error, "Failed to validate transaction base from daemon");
|
||||||
added_pool_txs.emplace_back(std::move(tx), pool_tx.tx_hash, pool_tx.double_spend_seen);
|
added_pool_txs.emplace_back(std::move(tx), pool_tx.tx_hash, pool_tx.double_spend_seen);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user