Merge #13603: bitcoin-tx: Stricter check for valid integers

57889e688d bitcoin-tx: Stricter check for valid integers (Daniel Kraft)

Pull request description:

  Just calling `atoi` to convert strings to integers does not check for valid integers very thoroughly; in particular, it just ignores everything starting from the first non-numeral character.  Even a string like "foo" is fine and silently returns 0.

  This meant that `bitcoin-tx` would not fail if such a string was passed in various places where an integer is expected (like the `locktime` or an input/output index); this means that it would, for instance, silently accept a typo and interpret it in an unexpected way.

  In this change, we use `ParseInt64` for parsing strings to integers, which actually verifies that the full string is valid as number.  New tests in the `bitcoin-util-test` cover the new error paths.

  This fixes #13599.

Tree-SHA512: 146a0af275e9f57784e5d0582d3defbac35551b54b6b7232f8a0b20db04aa611125e52aa4512ef2f8ed2cafc2a12fe586f9d10ed66d641cff090288f279b1988
This commit is contained in:
Wladimir J. van der Laan 2018-07-09 19:56:59 +02:00
commit 453ae5ec9f
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
2 changed files with 75 additions and 22 deletions

View file

@ -1,4 +1,4 @@
// Copyright (c) 2009-2017 The Bitcoin Core developers // Copyright (c) 2009-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
@ -193,18 +193,18 @@ static CAmount ExtractAndValidateValue(const std::string& strValue)
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
{ {
int64_t newVersion = atoi64(cmdVal); int64_t newVersion;
if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
throw std::runtime_error("Invalid TX version requested"); throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
tx.nVersion = (int) newVersion; tx.nVersion = (int) newVersion;
} }
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
{ {
int64_t newLocktime = atoi64(cmdVal); int64_t newLocktime;
if (newLocktime < 0LL || newLocktime > 0xffffffffLL) if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
throw std::runtime_error("Invalid TX locktime requested"); throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
tx.nLockTime = (unsigned int) newLocktime; tx.nLockTime = (unsigned int) newLocktime;
} }
@ -212,8 +212,8 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx) static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
{ {
// parse requested index // parse requested index
int inIdx = atoi(strInIdx); int64_t inIdx;
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
} }
@ -248,10 +248,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz); static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz);
// extract and validate vout // extract and validate vout
std::string strVout = vStrInputParts[1]; const std::string& strVout = vStrInputParts[1];
int vout = atoi(strVout); int64_t vout;
if ((vout < 0) || (vout > (int)maxVout)) if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
throw std::runtime_error("invalid TX input vout"); throw std::runtime_error("invalid TX input vout '" + strVout + "'");
// extract the optional sequence number // extract the optional sequence number
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max(); uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
@ -481,10 +481,9 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx) static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
{ {
// parse requested deletion index // parse requested deletion index
int inIdx = atoi(strInIdx); int64_t inIdx;
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
std::string strErr = "Invalid TX input index '" + strInIdx + "'"; throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
throw std::runtime_error(strErr.c_str());
} }
// delete input from transaction // delete input from transaction
@ -494,10 +493,9 @@ static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInId
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx) static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
{ {
// parse requested deletion index // parse requested deletion index
int outIdx = atoi(strOutIdx); int64_t outIdx;
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) { if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
std::string strErr = "Invalid TX output index '" + strOutIdx + "'"; throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
throw std::runtime_error(strErr.c_str());
} }
// delete output from transaction // delete output from transaction
@ -593,7 +591,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid"); uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid");
int nOut = atoi(prevOut["vout"].getValStr()); const int nOut = prevOut["vout"].get_int();
if (nOut < 0) if (nOut < 0)
throw std::runtime_error("vout must be positive"); throw std::runtime_error("vout must be positive");

View file

@ -26,6 +26,12 @@
"output_cmp": "blanktxv2.json", "output_cmp": "blanktxv2.json",
"description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)" "description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)"
}, },
{ "exec": "./bitcoin-tx",
"args": ["-create", "nversion=1foo"],
"return_code": 1,
"error_txt": "error: Invalid TX version requested",
"description": "Tests the check for invalid nversion value"
},
{ "exec": "./bitcoin-tx", { "exec": "./bitcoin-tx",
"args": ["-", "delin=1"], "args": ["-", "delin=1"],
"input": "tx394b54bb.hex", "input": "tx394b54bb.hex",
@ -45,6 +51,13 @@
"error_txt": "error: Invalid TX input index '31'", "error_txt": "error: Invalid TX input index '31'",
"description": "Attempts to delete an input with a bad index from a transaction. Expected to fail." "description": "Attempts to delete an input with a bad index from a transaction. Expected to fail."
}, },
{ "exec": "./bitcoin-tx",
"args": ["-", "delin=1foo"],
"input": "tx394b54bb.hex",
"return_code": 1,
"error_txt": "error: Invalid TX input index",
"description": "Tests the check for an invalid input index with delin"
},
{ "exec": "./bitcoin-tx", { "exec": "./bitcoin-tx",
"args": ["-", "delout=1"], "args": ["-", "delout=1"],
"input": "tx394b54bb.hex", "input": "tx394b54bb.hex",
@ -64,6 +77,13 @@
"error_txt": "error: Invalid TX output index '2'", "error_txt": "error: Invalid TX output index '2'",
"description": "Attempts to delete an output with a bad index from a transaction. Expected to fail." "description": "Attempts to delete an output with a bad index from a transaction. Expected to fail."
}, },
{ "exec": "./bitcoin-tx",
"args": ["-", "delout=1foo"],
"input": "tx394b54bb.hex",
"return_code": 1,
"error_txt": "error: Invalid TX output index",
"description": "Tests the check for an invalid output index with delout"
},
{ "exec": "./bitcoin-tx", { "exec": "./bitcoin-tx",
"args": ["-", "locktime=317000"], "args": ["-", "locktime=317000"],
"input": "tx394b54bb.hex", "input": "tx394b54bb.hex",
@ -76,6 +96,29 @@
"output_cmp": "tt-locktime317000-out.json", "output_cmp": "tt-locktime317000-out.json",
"description": "Adds an nlocktime to a transaction (output in json)" "description": "Adds an nlocktime to a transaction (output in json)"
}, },
{ "exec": "./bitcoin-tx",
"args": ["-create", "locktime=317000foo"],
"return_code": 1,
"error_txt": "error: Invalid TX locktime requested",
"description": "Tests the check for invalid locktime value"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
"replaceable=0foo"],
"return_code": 1,
"error_txt": "error: Invalid TX input index",
"description": "Tests the check for an invalid input index with replaceable"
},
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0x"],
"return_code": 1,
"error_txt": "error: invalid TX input vout",
"description": "Tests the check for an invalid vout value when adding an input"
},
{ "exec": "./bitcoin-tx", { "exec": "./bitcoin-tx",
"args": "args":
["-create", ["-create",
@ -225,6 +268,18 @@
"output_cmp": "txcreatesignv2.hex", "output_cmp": "txcreatesignv2.hex",
"description": "Creates a new transaction with a single input and a single output, and then signs the transaction" "description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
}, },
{ "exec": "./bitcoin-tx",
"args":
["-create",
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
"set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":\"0foo\",\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
"return_code": 1,
"error_txt": "error: prevtxs internal object typecheck fail",
"description": "Tests the check for invalid vout index in prevtxs for sign"
},
{ "exec": "./bitcoin-tx", { "exec": "./bitcoin-tx",
"args": "args":
["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"], ["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"],