Skip to content

Commit fad009a

Browse files
committed
Merge bitcoin/bitcoin#32520: Remove legacy Parse(U)Int*
faf55fc doc: Remove ParseInt mentions in documentation (MarcoFalke) 3333282 refactor: Remove unused Parse(U)Int* (MarcoFalke) fa84e6c bitcoin-tx: Reject + sign in MutateTxDel* (MarcoFalke) face251 bitcoin-tx: Reject + sign in vout parsing (MarcoFalke) fa8acaf bitcoin-tx: Reject + sign in replaceable parsing (MarcoFalke) faff25a bitcoin-tx: Reject + sign in locktime (MarcoFalke) dddd9e5 bitcoin-tx: Reject + sign in nversion parsing (MarcoFalke) fab06ac rest: Use SAFE_CHARS_URI in SanitizeString error msg (MarcoFalke) 8888bb4 rest: Reject + sign in /blockhashbyheight/ (MarcoFalke) fafd43c test: Reject + sign when parsing regtest deployment params (MarcoFalke) fa123af Reject + sign when checking -ipcfd (MarcoFalke) fa47985 Reject + sign in SplitHostPort (MarcoFalke) fab4c29 net: Reject + sign when parsing subnet mask (MarcoFalke) fa89652 init: Reject + sign in -*port parsing (MarcoFalke) fa9c455 cli: Reject + sign in -netinfo level parsing (MarcoFalke) fa98041 refactor: Use ToIntegral in CreateFromDump (MarcoFalke) fa23ed7 refactor: Use ToIntegral in ParseHDKeypath (MarcoFalke) Pull request description: The legacy int parsing is problematic, because it accepts the `+` sign for unsigned integers. In all cases this is either: * Useless, because the `+` sign was already rejected. * Erroneous and inconsistent, when third party parsers reject it. (C.f. bitcoin/bitcoin#32365) * Confusing, because the `+` sign is neither documented, nor can it be assumed to be present. Fix all issues by removing the legacy int parsing. ACKs for top commit: stickies-v: re-ACK faf55fc brunoerg: code review ACK faf55fc Tree-SHA512: a311ab6a58fe02a37741c1800feb3dcfad92377b4bfb61b433b2393f52ba89ef45d00940972b2767b213a3dd7b59e5e35d5b659c586eacdfe4e565a77b12b19f
2 parents 0f9baba + faf55fc commit fad009a

18 files changed

+132
-395
lines changed

doc/developer-notes.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,10 +1009,6 @@ Strings and formatting
10091009
buffer overflows, and surprises with `\0` characters. Also, some C string manipulations
10101010
tend to act differently depending on platform, or even the user locale.
10111011
1012-
- Use `ToIntegral` from [`strencodings.h`](/src/util/strencodings.h) for number parsing. In legacy code you might also find `ParseInt*` family of functions, `ParseDouble` or `LocaleIndependentAtoi`.
1013-
1014-
- *Rationale*: These functions do overflow checking and avoid pesky locale issues.
1015-
10161012
- For `strprintf`, `LogInfo`, `LogDebug`, etc formatting characters don't need size specifiers.
10171013
10181014
- *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion.

src/bitcoin-cli.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2009-2010 Satoshi Nakamoto
2-
// Copyright (c) 2009-2022 The Bitcoin Core developers
2+
// Copyright (c) 2009-present The Bitcoin Core developers
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

@@ -472,7 +472,8 @@ class NetinfoRequestHandler : public BaseRequestHandler
472472
{
473473
if (!args.empty()) {
474474
uint8_t n{0};
475-
if (ParseUInt8(args.at(0), &n)) {
475+
if (const auto res{ToIntegral<uint8_t>(args.at(0))}) {
476+
n = *res;
476477
m_details_level = std::min(n, NETINFO_MAX_LEVEL);
477478
} else {
478479
throw std::runtime_error(strprintf("invalid -netinfo level argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0)));

src/bitcoin-tx.cpp

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -211,35 +211,33 @@ static CAmount ExtractAndValidateValue(const std::string& strValue)
211211

212212
static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
213213
{
214-
uint32_t newVersion;
215-
if (!ParseUInt32(cmdVal, &newVersion) || newVersion < 1 || newVersion > TX_MAX_STANDARD_VERSION) {
214+
const auto ver{ToIntegral<uint32_t>(cmdVal)};
215+
if (!ver || *ver < 1 || *ver > TX_MAX_STANDARD_VERSION) {
216216
throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
217217
}
218-
219-
tx.version = newVersion;
218+
tx.version = *ver;
220219
}
221220

222221
static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
223222
{
224-
int64_t newLocktime;
225-
if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
223+
const auto locktime{ToIntegral<uint32_t>(cmdVal)};
224+
if (!locktime) {
226225
throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
227-
228-
tx.nLockTime = (unsigned int) newLocktime;
226+
}
227+
tx.nLockTime = *locktime;
229228
}
230229

231230
static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx)
232231
{
233-
// parse requested index
234-
int64_t inIdx = -1;
235-
if (strInIdx != "" && (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size()))) {
232+
const auto idx{ToIntegral<uint32_t>(strInIdx)};
233+
if (strInIdx != "" && (!idx || *idx >= tx.vin.size())) {
236234
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
237235
}
238236

239237
// set the nSequence to MAX_INT - 2 (= RBF opt in flag)
240-
int cnt = 0;
238+
uint32_t cnt{0};
241239
for (CTxIn& txin : tx.vin) {
242-
if (strInIdx == "" || cnt == inIdx) {
240+
if (strInIdx == "" || cnt == *idx) {
243241
if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
244242
txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
245243
}
@@ -277,9 +275,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
277275

278276
// extract and validate vout
279277
const std::string& strVout = vStrInputParts[1];
280-
int64_t vout;
281-
if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
278+
const auto vout{ToIntegral<uint32_t>(strVout)};
279+
if (!vout || *vout > maxVout) {
282280
throw std::runtime_error("invalid TX input vout '" + strVout + "'");
281+
}
283282

284283
// extract the optional sequence number
285284
uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
@@ -288,7 +287,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu
288287
}
289288

290289
// append to transaction input list
291-
CTxIn txin(*txid, vout, CScript(), nSequenceIn);
290+
CTxIn txin{*txid, *vout, CScript{}, nSequenceIn};
292291
tx.vin.push_back(txin);
293292
}
294293

@@ -508,26 +507,20 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str
508507

509508
static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
510509
{
511-
// parse requested deletion index
512-
int64_t inIdx;
513-
if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast<int64_t>(tx.vin.size())) {
510+
const auto idx{ToIntegral<uint32_t>(strInIdx)};
511+
if (!idx || idx >= tx.vin.size()) {
514512
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
515513
}
516-
517-
// delete input from transaction
518-
tx.vin.erase(tx.vin.begin() + inIdx);
514+
tx.vin.erase(tx.vin.begin() + *idx);
519515
}
520516

521517
static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
522518
{
523-
// parse requested deletion index
524-
int64_t outIdx;
525-
if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast<int64_t>(tx.vout.size())) {
519+
const auto idx{ToIntegral<uint32_t>(strOutIdx)};
520+
if (!idx || idx >= tx.vout.size()) {
526521
throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'");
527522
}
528-
529-
// delete output from transaction
530-
tx.vout.erase(tx.vout.begin() + outIdx);
523+
tx.vout.erase(tx.vout.begin() + *idx);
531524
}
532525

533526
static const unsigned int N_SIGHASH_OPTS = 7;

src/chainparams.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti
5353
}
5454

5555
const auto value{arg.substr(found + 1)};
56-
int32_t height;
57-
if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits<int>::max()) {
56+
const auto height{ToIntegral<int32_t>(value)};
57+
if (!height || *height < 0 || *height >= std::numeric_limits<int>::max()) {
5858
throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg));
5959
}
6060

6161
const auto deployment_name{arg.substr(0, found)};
6262
if (const auto buried_deployment = GetBuriedDeployment(deployment_name)) {
63-
options.activation_heights[*buried_deployment] = height;
63+
options.activation_heights[*buried_deployment] = *height;
6464
} else {
6565
throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg));
6666
}
@@ -72,16 +72,22 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti
7272
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
7373
}
7474
CChainParams::VersionBitsParameters vbparams{};
75-
if (!ParseInt64(vDeploymentParams[1], &vbparams.start_time)) {
75+
const auto start_time{ToIntegral<int64_t>(vDeploymentParams[1])};
76+
if (!start_time) {
7677
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
7778
}
78-
if (!ParseInt64(vDeploymentParams[2], &vbparams.timeout)) {
79+
vbparams.start_time = *start_time;
80+
const auto timeout{ToIntegral<int64_t>(vDeploymentParams[2])};
81+
if (!timeout) {
7982
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
8083
}
84+
vbparams.timeout = *timeout;
8185
if (vDeploymentParams.size() >= 4) {
82-
if (!ParseInt32(vDeploymentParams[3], &vbparams.min_activation_height)) {
86+
const auto min_activation_height{ToIntegral<int64_t>(vDeploymentParams[3])};
87+
if (!min_activation_height) {
8388
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
8489
}
90+
vbparams.min_activation_height = *min_activation_height;
8591
} else {
8692
vbparams.min_activation_height = 0;
8793
}

src/init.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,8 +1151,8 @@ bool CheckHostPortOptions(const ArgsManager& args) {
11511151
"-rpcport",
11521152
}) {
11531153
if (const auto port{args.GetArg(port_option)}) {
1154-
uint16_t n;
1155-
if (!ParseUInt16(*port, &n) || n == 0) {
1154+
const auto n{ToIntegral<uint16_t>(*port)};
1155+
if (!n || *n == 0) {
11561156
return InitError(InvalidPortErrMsg(port_option, *port));
11571157
}
11581158
}

src/ipc/process.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ class ProcessImpl : public Process
5555
// in combination with other arguments because the parent process
5656
// should be able to control the child process through the IPC protocol
5757
// without passing information out of band.
58-
if (!ParseInt32(argv[2], &fd)) {
58+
const auto maybe_fd{ToIntegral<int32_t>(argv[2])};
59+
if (!maybe_fd) {
5960
throw std::runtime_error(strprintf("Invalid -ipcfd number '%s'", argv[2]));
6061
}
62+
fd = *maybe_fd;
6163
return true;
6264
}
6365
int connect(const fs::path& data_dir,

src/netbase.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -829,10 +829,9 @@ CSubNet LookupSubNet(const std::string& subnet_str)
829829
addr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
830830
if (slash_pos != subnet_str.npos) {
831831
const std::string netmask_str{subnet_str.substr(slash_pos + 1)};
832-
uint8_t netmask;
833-
if (ParseUInt8(netmask_str, &netmask)) {
832+
if (const auto netmask{ToIntegral<uint8_t>(netmask_str)}) {
834833
// Valid number; assume CIDR variable-length subnet masking.
835-
subnet = CSubNet{addr.value(), netmask};
834+
subnet = CSubNet{addr.value(), *netmask};
836835
} else {
837836
// Invalid number; try full netmask syntax. Never allow lookup for netmask.
838837
const std::optional<CNetAddr> full_netmask{LookupHost(netmask_str, /*fAllowLookup=*/false)};

src/rest.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -962,9 +962,9 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
962962
std::string height_str;
963963
const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part);
964964

965-
int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785
966-
if (!ParseInt32(height_str, &blockheight) || blockheight < 0) {
967-
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str));
965+
const auto blockheight{ToIntegral<int32_t>(height_str)};
966+
if (!blockheight || *blockheight < 0) {
967+
return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str, SAFE_CHARS_URI));
968968
}
969969

970970
CBlockIndex* pblockindex = nullptr;
@@ -974,10 +974,10 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req,
974974
ChainstateManager& chainman = *maybe_chainman;
975975
LOCK(cs_main);
976976
const CChain& active_chain = chainman.ActiveChain();
977-
if (blockheight > active_chain.Height()) {
977+
if (*blockheight > active_chain.Height()) {
978978
return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range");
979979
}
980-
pblockindex = active_chain[blockheight];
980+
pblockindex = active_chain[*blockheight];
981981
}
982982
switch (rf) {
983983
case RESTResponseFormat::BINARY: {

src/test/fuzz/locale.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,35 +46,15 @@ FUZZ_TARGET(locale)
4646
assert(c_locale != nullptr);
4747

4848
const std::string random_string = fuzzed_data_provider.ConsumeRandomLengthString(5);
49-
int32_t parseint32_out_without_locale;
50-
const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale);
51-
int64_t parseint64_out_without_locale;
52-
const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale);
5349
const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral<int64_t>();
5450
const std::string tostring_without_locale = util::ToString(random_int64);
55-
// The variable `random_int32` is no longer used, but the harness still needs to
56-
// consume the same data that it did previously to not invalidate existing seeds.
57-
const int32_t random_int32 = fuzzed_data_provider.ConsumeIntegral<int32_t>();
58-
(void)random_int32;
5951
const std::string strprintf_int_without_locale = strprintf("%d", random_int64);
6052
const double random_double = fuzzed_data_provider.ConsumeFloatingPoint<double>();
6153
const std::string strprintf_double_without_locale = strprintf("%f", random_double);
6254

6355
const char* new_locale = std::setlocale(LC_ALL, locale_identifier.c_str());
6456
assert(new_locale != nullptr);
6557

66-
int32_t parseint32_out_with_locale;
67-
const bool parseint32_with_locale = ParseInt32(random_string, &parseint32_out_with_locale);
68-
assert(parseint32_without_locale == parseint32_with_locale);
69-
if (parseint32_without_locale) {
70-
assert(parseint32_out_without_locale == parseint32_out_with_locale);
71-
}
72-
int64_t parseint64_out_with_locale;
73-
const bool parseint64_with_locale = ParseInt64(random_string, &parseint64_out_with_locale);
74-
assert(parseint64_without_locale == parseint64_with_locale);
75-
if (parseint64_without_locale) {
76-
assert(parseint64_out_without_locale == parseint64_out_with_locale);
77-
}
7858
const std::string tostring_with_locale = util::ToString(random_int64);
7959
assert(tostring_without_locale == tostring_with_locale);
8060
const std::string strprintf_int_with_locale = strprintf("%d", random_int64);

src/test/fuzz/parse_numbers.cpp

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,59 @@
55
#include <test/fuzz/fuzz.h>
66
#include <util/moneystr.h>
77
#include <util/strencodings.h>
8+
#include <util/string.h>
89

10+
#include <cassert>
11+
#include <cstdint>
12+
#include <optional>
913
#include <string>
1014

1115
FUZZ_TARGET(parse_numbers)
1216
{
1317
const std::string random_string(buffer.begin(), buffer.end());
18+
{
19+
const auto i8{ToIntegral<int8_t>(random_string)};
20+
const auto u8{ToIntegral<uint8_t>(random_string)};
21+
const auto i16{ToIntegral<int16_t>(random_string)};
22+
const auto u16{ToIntegral<uint16_t>(random_string)};
23+
const auto i32{ToIntegral<int32_t>(random_string)};
24+
const auto u32{ToIntegral<uint32_t>(random_string)};
25+
const auto i64{ToIntegral<int64_t>(random_string)};
26+
const auto u64{ToIntegral<uint64_t>(random_string)};
27+
// Dont check any values, just that each success result must fit into
28+
// the one with the largest bit-width.
29+
if (i8) {
30+
assert(i8 == i64);
31+
}
32+
if (u8) {
33+
assert(u8 == u64);
34+
}
35+
if (i16) {
36+
assert(i16 == i64);
37+
}
38+
if (u16) {
39+
assert(u16 == u64);
40+
}
41+
if (i32) {
42+
assert(i32 == i64);
43+
}
44+
if (u32) {
45+
assert(u32 == u64);
46+
}
47+
constexpr auto digits{"0123456789"};
48+
if (i64) {
49+
assert(util::RemovePrefixView(random_string, "-").find_first_not_of(digits) == std::string::npos);
50+
}
51+
if (u64) {
52+
assert(random_string.find_first_not_of(digits) == std::string::npos);
53+
}
54+
}
1455

1556
(void)ParseMoney(random_string);
1657

17-
uint8_t u8;
18-
(void)ParseUInt8(random_string, &u8);
19-
20-
uint16_t u16;
21-
(void)ParseUInt16(random_string, &u16);
22-
23-
int32_t i32;
24-
(void)ParseInt32(random_string, &i32);
2558
(void)LocaleIndependentAtoi<int>(random_string);
2659

27-
uint32_t u32;
28-
(void)ParseUInt32(random_string, &u32);
29-
3060
int64_t i64;
3161
(void)LocaleIndependentAtoi<int64_t>(random_string);
3262
(void)ParseFixedPoint(random_string, 3, &i64);
33-
(void)ParseInt64(random_string, &i64);
34-
35-
uint64_t u64;
36-
(void)ParseUInt64(random_string, &u64);
3763
}

0 commit comments

Comments
 (0)