diff --git a/src/core_read.cpp b/src/core_read.cpp --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -179,6 +179,29 @@ return result; } +// Check that all of the input and output scripts of a transaction contains +// valid opcodes +bool CheckTxScriptsSanity(const CMutableTransaction &tx) { + // Check input scripts for non-coinbase txs + if (!CTransaction(tx).IsCoinBase()) { + for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (!tx.vin[i].scriptSig.HasValidOps() || + tx.vin[i].scriptSig.size() > MAX_SCRIPT_SIZE) { + return false; + } + } + } + // Check output scripts + for (unsigned int i = 0; i < tx.vout.size(); i++) { + if (!tx.vout[i].scriptPubKey.HasValidOps() || + tx.vout[i].scriptPubKey.size() > MAX_SCRIPT_SIZE) { + return false; + } + } + + return true; +} + bool DecodeHexTx(CMutableTransaction &tx, const std::string &strHexTx) { if (!IsHex(strHexTx)) { return false; @@ -189,7 +212,7 @@ CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); try { ssData >> tx; - if (!ssData.empty()) { + if (!ssData.empty() && CheckTxScriptsSanity(tx)) { return false; } } catch (const std::exception &) { diff --git a/src/script/script.h b/src/script/script.h --- a/src/script/script.h +++ b/src/script/script.h @@ -198,6 +198,9 @@ OP_INVALIDOPCODE = 0xff, }; +// Maximum value that an opcode can be +static const unsigned int MAX_OPCODE = OP_NOP10; + const char *GetOpName(opcodetype opcode); class scriptnum_error : public std::runtime_error { @@ -653,6 +656,9 @@ bool IsPushOnly(const_iterator pc) const; bool IsPushOnly() const; + /** Check if the script contains valid OP_CODES */ + bool HasValidOps() const; + /** * Returns whether the script is guaranteed to fail at execution, regardless * of the initial stack. This allows outputs to be pruned instantly when diff --git a/src/script/script.cpp b/src/script/script.cpp --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -483,3 +483,16 @@ bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); } + +bool CScript::HasValidOps() const { + CScript::const_iterator it = begin(); + while (it < end()) { + opcodetype opcode; + std::vector item; + if (!GetOp(it, opcode, item) || opcode > MAX_OPCODE || + item.size() > MAX_SCRIPT_ELEMENT_SIZE) { + return false; + } + } + return true; +} diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2573,4 +2573,19 @@ BOOST_CHECK(s == d); } +BOOST_AUTO_TEST_CASE(script_HasValidOps) { + // Exercise the HasValidOps functionality + CScript script; + script = ScriptFromHex( + "76a9141234567890abcdefa1a2a3a4a5a6a7a8a9a0aaab88ac"); // Normal script + BOOST_CHECK(script.HasValidOps()); + script = + ScriptFromHex("76a914ff34567890abcdefa1a2a3a4a5a6a7a8a9a0aaab88ac"); + BOOST_CHECK(script.HasValidOps()); + script = ScriptFromHex("ff88ac"); // Script with OP_INVALIDOPCODE explicit + BOOST_CHECK(!script.HasValidOps()); + script = ScriptFromHex("88acc0"); // Script with undefined opcode + BOOST_CHECK(!script.HasValidOps()); +} + BOOST_AUTO_TEST_SUITE_END()