Skip to content

Commit 7cc00bf

Browse files
committed
Merge bitcoin/bitcoin#30436: fix: Make TxidFromString() respect string_view length
09ce350 fix: Make TxidFromString() respect string_view length (Hodlinator) 01e314c refactor: Change base_blob::SetHex() to take std::string_view (Hodlinator) 2f5577d test: uint256 - Garbage suffixes and zero padding (Hodlinator) f11f816 refactor: Make uint256_tests no longer use deprecated BOOST_CHECK() (Hodlinator) f0eeee2 test: Add test for TxidFromString() behavior (Ryan Ofsky) Pull request description: ### Problem Prior to this, `TxidFromString()` was passing `string_view::data()` into `uint256S()` which meant it would only receive the a naked `char*` pointer and potentially scan past the `string_view::length()` until it found a null terminator (or some other non-hex character). Appears to have been a fully dormant bug as callers were either passing a string literal or `std::string` directly to `TxidFromFromString()`, meaning a null terminator always existed at `pointer[length()]`. Bug existed since original merge of `TxidFromString()`. ### Solution Make `uint256S()` (and `base_blob::SetHex()`) take and operate on `std::string_view` instead of `const char*` and have `TxidFromString()` pass that in. (PR was prompted by comment in bitcoin/bitcoin#30377 (comment) (referring to bitcoin/bitcoin#28922 (comment))). ACKs for top commit: maflcko: re-ACK 09ce350 🕓 paplorinc: ACK 09ce350 ryanofsky: Code review ACK 09ce350. I think the current code changes are about as small as you could make to fix the bug without introducing a string copy, and the surrounding test improvements are all very nice and welcome. Tree-SHA512: c2c10551785fb6688d1e2492ba42a8eee4c19abbe8461bb0774d56a70c23cd6b0718d2641632890bee880c06202dee148126447dd2264eaed4f5fee7e1bcb581
2 parents ed2d775 + 09ce350 commit 7cc00bf

File tree

8 files changed

+192
-184
lines changed

8 files changed

+192
-184
lines changed

src/test/transaction_tests.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,4 +1028,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
10281028
}
10291029
}
10301030

1031+
BOOST_AUTO_TEST_CASE(test_TxidFromString)
1032+
{
1033+
// Make sure TxidFromString respects string_view length and stops reading at
1034+
// end of the substring.
1035+
BOOST_CHECK_EQUAL(TxidFromString(std::string_view("ABCD1234", 4)).ToString(),
1036+
"000000000000000000000000000000000000000000000000000000000000abcd");
1037+
}
1038+
10311039
BOOST_AUTO_TEST_SUITE_END()

src/test/txpackage_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ inline CTransactionRef create_placeholder_tx(size_t num_inputs, size_t num_outpu
4747
// Create a Wtxid from a hex string
4848
inline Wtxid WtxidFromString(std::string_view str)
4949
{
50-
return Wtxid::FromUint256(uint256S(str.data()));
50+
return Wtxid::FromUint256(uint256S(str));
5151
}
5252

5353
BOOST_FIXTURE_TEST_CASE(package_hash_tests, TestChain100Setup)

src/test/uint256_tests.cpp

Lines changed: 147 additions & 142 deletions
Large diffs are not rendered by default.

src/test/util/setup_common.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr;
7979
/** Random context to get unique temp data dirs. Separate from g_insecure_rand_ctx, which can be seeded from a const env var */
8080
static FastRandomContext g_insecure_rand_ctx_temp_path;
8181

82+
std::ostream& operator<<(std::ostream& os, const arith_uint256& num)
83+
{
84+
os << ArithToUint256(num).ToString();
85+
return os;
86+
}
87+
88+
std::ostream& operator<<(std::ostream& os, const uint160& num)
89+
{
90+
os << num.ToString();
91+
return os;
92+
}
93+
8294
std::ostream& operator<<(std::ostream& os, const uint256& num)
8395
{
8496
os << num.ToString();

src/test/util/setup_common.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <type_traits>
2525
#include <vector>
2626

27+
class arith_uint256;
2728
class CFeeRate;
2829
class Chainstate;
2930
class FastRandomContext;
@@ -236,7 +237,9 @@ std::unique_ptr<T> MakeNoLogFileContext(const ChainType chain_type = ChainType::
236237

237238
CBlock getBlock13b8a();
238239

239-
// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_*
240+
// Make types usable in BOOST_CHECK_*
241+
std::ostream& operator<<(std::ostream& os, const arith_uint256& num);
242+
std::ostream& operator<<(std::ostream& os, const uint160& num);
240243
std::ostream& operator<<(std::ostream& os, const uint256& num);
241244

242245
/**

src/uint256.cpp

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,39 +18,31 @@ std::string base_blob<BITS>::GetHex() const
1818
}
1919

2020
template <unsigned int BITS>
21-
void base_blob<BITS>::SetHex(const char* psz)
21+
void base_blob<BITS>::SetHex(const std::string_view str)
2222
{
2323
std::fill(m_data.begin(), m_data.end(), 0);
2424

25-
// skip leading spaces
26-
while (IsSpace(*psz))
27-
psz++;
25+
const auto trimmed = util::RemovePrefixView(util::TrimStringView(str), "0x");
2826

29-
// skip 0x
30-
if (psz[0] == '0' && ToLower(psz[1]) == 'x')
31-
psz += 2;
32-
33-
// hex string to uint
27+
// Note: if we are passed a greater number of digits than would fit as bytes
28+
// in m_data, we will be discarding the leftmost ones.
29+
// str="12bc" in a WIDTH=1 m_data => m_data[] == "\0xbc", not "0x12".
3430
size_t digits = 0;
35-
while (::HexDigit(psz[digits]) != -1)
36-
digits++;
31+
for (const char c : trimmed) {
32+
if (::HexDigit(c) == -1) break;
33+
++digits;
34+
}
3735
unsigned char* p1 = m_data.data();
3836
unsigned char* pend = p1 + WIDTH;
3937
while (digits > 0 && p1 < pend) {
40-
*p1 = ::HexDigit(psz[--digits]);
38+
*p1 = ::HexDigit(trimmed[--digits]);
4139
if (digits > 0) {
42-
*p1 |= ((unsigned char)::HexDigit(psz[--digits]) << 4);
40+
*p1 |= ((unsigned char)::HexDigit(trimmed[--digits]) << 4);
4341
p1++;
4442
}
4543
}
4644
}
4745

48-
template <unsigned int BITS>
49-
void base_blob<BITS>::SetHex(const std::string& str)
50-
{
51-
SetHex(str.c_str());
52-
}
53-
5446
template <unsigned int BITS>
5547
std::string base_blob<BITS>::ToString() const
5648
{
@@ -60,14 +52,12 @@ std::string base_blob<BITS>::ToString() const
6052
// Explicit instantiations for base_blob<160>
6153
template std::string base_blob<160>::GetHex() const;
6254
template std::string base_blob<160>::ToString() const;
63-
template void base_blob<160>::SetHex(const char*);
64-
template void base_blob<160>::SetHex(const std::string&);
55+
template void base_blob<160>::SetHex(std::string_view);
6556

6657
// Explicit instantiations for base_blob<256>
6758
template std::string base_blob<256>::GetHex() const;
6859
template std::string base_blob<256>::ToString() const;
69-
template void base_blob<256>::SetHex(const char*);
70-
template void base_blob<256>::SetHex(const std::string&);
60+
template void base_blob<256>::SetHex(std::string_view);
7161

7262
const uint256 uint256::ZERO(0);
7363
const uint256 uint256::ONE(1);

src/uint256.h

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ class base_blob
5757
friend constexpr bool operator!=(const base_blob& a, const base_blob& b) { return a.Compare(b) != 0; }
5858
friend constexpr bool operator<(const base_blob& a, const base_blob& b) { return a.Compare(b) < 0; }
5959

60+
// Hex string representations are little-endian.
6061
std::string GetHex() const;
61-
void SetHex(const char* psz);
62-
void SetHex(const std::string& str);
62+
void SetHex(std::string_view str);
6363
std::string ToString() const;
6464

6565
constexpr const unsigned char* data() const { return m_data.data(); }
@@ -112,21 +112,11 @@ class uint256 : public base_blob<256> {
112112
static const uint256 ONE;
113113
};
114114

115-
/* uint256 from const char *.
116-
* This is a separate function because the constructor uint256(const char*) can result
117-
* in dangerously catching uint256(0).
115+
/* uint256 from std::string_view, treated as little-endian.
116+
* This is not a uint256 constructor because of historical fears of uint256(0)
117+
* resolving to a NULL string and crashing.
118118
*/
119-
inline uint256 uint256S(const char *str)
120-
{
121-
uint256 rv;
122-
rv.SetHex(str);
123-
return rv;
124-
}
125-
/* uint256 from std::string.
126-
* This is a separate function because the constructor uint256(const std::string &str) can result
127-
* in dangerously catching uint256(0) via std::string(const char*).
128-
*/
129-
inline uint256 uint256S(const std::string& str)
119+
inline uint256 uint256S(std::string_view str)
130120
{
131121
uint256 rv;
132122
rv.SetHex(str);

src/util/transaction_identifier.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ using Wtxid = transaction_identifier<true>;
6868

6969
inline Txid TxidFromString(std::string_view str)
7070
{
71-
return Txid::FromUint256(uint256S(str.data()));
71+
return Txid::FromUint256(uint256S(str));
7272
}
7373

7474
#endif // BITCOIN_UTIL_TRANSACTION_IDENTIFIER_H

0 commit comments

Comments
 (0)