Skip to content

Commit 453ae5e

Browse files
committed
Merge #13603: bitcoin-tx: Stricter check for valid integers
57889e6 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
2 parents a6d14b1 + 57889e6 commit 453ae5e

File tree

2 files changed

+75
-22
lines changed

2 files changed

+75
-22
lines changed

src/bitcoin-tx.cpp

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2009-2017 The Bitcoin Core developers
1+
// Copyright (c) 2009-2018 The Bitcoin Core developers
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

@@ -193,27 +193,27 @@ static CAmount ExtractAndValidateValue(const std::string& strValue)
193193

194194
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
195195
{
196-
int64_t newVersion = atoi64(cmdVal);
197-
if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
198-
throw std::runtime_error("Invalid TX version requested");
196+
int64_t newVersion;
197+
if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION)
198+
throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
199199

200200
tx.nVersion = (int) newVersion;
201201
}
202202

203203
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
204204
{
205-
int64_t newLocktime = atoi64(cmdVal);
206-
if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
207-
throw std::runtime_error("Invalid TX locktime requested");
205+
int64_t newLocktime;
206+
if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
207+
throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
208208

209209
tx.nLockTime = (unsigned int) newLocktime;
210210
}
211211

212212
static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
213213
{
214214
// parse requested index
215-
int inIdx = atoi(strInIdx);
216-
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
215+
int64_t inIdx;
216+
if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
217217
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
218218
}
219219

@@ -248,10 +248,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
248248
static const unsigned int maxVout = MAX_BLOCK_WEIGHT / (WITNESS_SCALE_FACTOR * minTxOutSz);
249249

250250
// extract and validate vout
251-
std::string strVout = vStrInputParts[1];
252-
int vout = atoi(strVout);
253-
if ((vout < 0) || (vout > (int)maxVout))
254-
throw std::runtime_error("invalid TX input vout");
251+
const std::string& strVout = vStrInputParts[1];
252+
int64_t vout;
253+
if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
254+
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
255255

256256
// extract the optional sequence number
257257
uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max();
@@ -481,10 +481,9 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
481481
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
482482
{
483483
// parse requested deletion index
484-
int inIdx = atoi(strInIdx);
485-
if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
486-
std::string strErr = "Invalid TX input index '" + strInIdx + "'";
487-
throw std::runtime_error(strErr.c_str());
484+
int64_t inIdx;
485+
if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
486+
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
488487
}
489488

490489
// delete input from transaction
@@ -494,10 +493,9 @@ static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInId
494493
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
495494
{
496495
// parse requested deletion index
497-
int outIdx = atoi(strOutIdx);
498-
if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
499-
std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
500-
throw std::runtime_error(strErr.c_str());
496+
int64_t outIdx;
497+
if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
498+
throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
501499
}
502500

503501
// delete output from transaction
@@ -593,7 +591,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
593591

594592
uint256 txid = ParseHashStr(prevOut["txid"].get_str(), "txid");
595593

596-
int nOut = atoi(prevOut["vout"].getValStr());
594+
const int nOut = prevOut["vout"].get_int();
597595
if (nOut < 0)
598596
throw std::runtime_error("vout must be positive");
599597

test/util/data/bitcoin-util-test.json

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
"output_cmp": "blanktxv2.json",
2727
"description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)"
2828
},
29+
{ "exec": "./bitcoin-tx",
30+
"args": ["-create", "nversion=1foo"],
31+
"return_code": 1,
32+
"error_txt": "error: Invalid TX version requested",
33+
"description": "Tests the check for invalid nversion value"
34+
},
2935
{ "exec": "./bitcoin-tx",
3036
"args": ["-", "delin=1"],
3137
"input": "tx394b54bb.hex",
@@ -45,6 +51,13 @@
4551
"error_txt": "error: Invalid TX input index '31'",
4652
"description": "Attempts to delete an input with a bad index from a transaction. Expected to fail."
4753
},
54+
{ "exec": "./bitcoin-tx",
55+
"args": ["-", "delin=1foo"],
56+
"input": "tx394b54bb.hex",
57+
"return_code": 1,
58+
"error_txt": "error: Invalid TX input index",
59+
"description": "Tests the check for an invalid input index with delin"
60+
},
4861
{ "exec": "./bitcoin-tx",
4962
"args": ["-", "delout=1"],
5063
"input": "tx394b54bb.hex",
@@ -64,6 +77,13 @@
6477
"error_txt": "error: Invalid TX output index '2'",
6578
"description": "Attempts to delete an output with a bad index from a transaction. Expected to fail."
6679
},
80+
{ "exec": "./bitcoin-tx",
81+
"args": ["-", "delout=1foo"],
82+
"input": "tx394b54bb.hex",
83+
"return_code": 1,
84+
"error_txt": "error: Invalid TX output index",
85+
"description": "Tests the check for an invalid output index with delout"
86+
},
6787
{ "exec": "./bitcoin-tx",
6888
"args": ["-", "locktime=317000"],
6989
"input": "tx394b54bb.hex",
@@ -76,6 +96,29 @@
7696
"output_cmp": "tt-locktime317000-out.json",
7797
"description": "Adds an nlocktime to a transaction (output in json)"
7898
},
99+
{ "exec": "./bitcoin-tx",
100+
"args": ["-create", "locktime=317000foo"],
101+
"return_code": 1,
102+
"error_txt": "error: Invalid TX locktime requested",
103+
"description": "Tests the check for invalid locktime value"
104+
},
105+
{ "exec": "./bitcoin-tx",
106+
"args":
107+
["-create",
108+
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
109+
"replaceable=0foo"],
110+
"return_code": 1,
111+
"error_txt": "error: Invalid TX input index",
112+
"description": "Tests the check for an invalid input index with replaceable"
113+
},
114+
{ "exec": "./bitcoin-tx",
115+
"args":
116+
["-create",
117+
"in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0x"],
118+
"return_code": 1,
119+
"error_txt": "error: invalid TX input vout",
120+
"description": "Tests the check for an invalid vout value when adding an input"
121+
},
79122
{ "exec": "./bitcoin-tx",
80123
"args":
81124
["-create",
@@ -225,6 +268,18 @@
225268
"output_cmp": "txcreatesignv2.hex",
226269
"description": "Creates a new transaction with a single input and a single output, and then signs the transaction"
227270
},
271+
{ "exec": "./bitcoin-tx",
272+
"args":
273+
["-create",
274+
"in=4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485:0",
275+
"set=privatekeys:[\"5HpHagT65TZzG1PH3CSu63k8DbpvD8s5ip4nEB3kEsreAnchuDf\"]",
276+
"set=prevtxs:[{\"txid\":\"4d49a71ec9da436f71ec4ee231d04f292a29cd316f598bb7068feccabdc59485\",\"vout\":\"0foo\",\"scriptPubKey\":\"76a91491b24bf9f5288532960ac687abb035127b1d28a588ac\"}]",
277+
"sign=ALL",
278+
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
279+
"return_code": 1,
280+
"error_txt": "error: prevtxs internal object typecheck fail",
281+
"description": "Tests the check for invalid vout index in prevtxs for sign"
282+
},
228283
{ "exec": "./bitcoin-tx",
229284
"args":
230285
["-create", "outpubkey=0:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397", "nversion=1"],

0 commit comments

Comments
 (0)