Skip to content

Commit d0e777b

Browse files
committed
Merge bitcoin/bitcoin#36111: rpc: bound memory for overlong Bech32 errors
7fcaccd bech32: bound overlength error locations (Lőrinc) Pull request description: **Problem:** `validateaddress` reports likely error positions for invalid Bech32 inputs, including multiple useful positions for character and checksum errors. For an overlength input, `LocateErrors()` returns every position after the 90-character limit, which the RPC converts to a `UniValue` number before serializing the response. A near-limit authenticated request therefore creates about 33 million `int` values and 33 million `UniValue` objects. **Fix:** Return position 90 for an overlength input, which identifies where the single length violation begins. Character and checksum errors continue to return multiple useful positions when they can be determined. The tests now include an oversized example and pin the bounded result. **Reproducer:** Peak memory usage for a near-limit authenticated request: <details> <summary>Linux reproducer</summary> ```bash sed -i "/def test_validateaddress(self):/a\\ self.nodes[0].validateaddress('bcrt1' + 'q' * (2**25 - 100))\\ __import__('time').sleep(30)" test/functional/rpc_invalid_address_message.py cmake -B build && cmake --build build -j2 build/test/functional/rpc_invalid_address_message.py >/dev/null 2>&1 & sleep 20 && awk '/VmHWM/' /proc/$(pgrep bitcoind)/status ``` </details> ```text Before ████████████████████████ 5.69 GiB After █░░░░░░░░░░░░░░░░░░░░░░░ 240 MiB ``` ACKs for top commit: maflcko: lgtm ACK 7fcaccd sedited: ACK 7fcaccd janb84: ACK 7fcaccd Tree-SHA512: 3d439774d394f081b8107f8131963f7aa23ed048b0d6d349a80f9b3481fefeef7b5ce239fbd33606ad1f4960e6bfd899f968c50febc70d38b2fe731c6049583f
2 parents 05e49b3 + 7fcaccd commit d0e777b

3 files changed

Lines changed: 4 additions & 4 deletions

File tree

src/bech32.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
#include <array>
1010
#include <cassert>
11-
#include <numeric>
1211
#include <optional>
1312

1413
namespace bech32
@@ -404,8 +403,7 @@ std::pair<std::string, std::vector<int>> LocateErrors(const std::string& str, Ch
404403
std::vector<int> error_locations{};
405404

406405
if (str.size() > limit) {
407-
error_locations.resize(str.size() - limit);
408-
std::iota(error_locations.begin(), error_locations.end(), static_cast<int>(limit));
406+
error_locations.push_back(static_cast<int>(limit));
409407
return std::make_pair("Bech32 string too long", std::move(error_locations));
410408
}
411409

src/test/bech32_tests.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
7171
"A12uEL5L",
7272
"abcdef1qpzrz9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
7373
"test1zg69w7y6hn0aqy352euf40x77qddq3dc",
74+
std::string(100, 'q'),
7475
};
7576
static const std::pair<std::string, std::vector<int>> ERRORS[] = {
7677
{"Invalid character or mixed case", {0}},
@@ -89,6 +90,7 @@ BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
8990
{"Invalid character or mixed case", {3}},
9091
{"Invalid Bech32 checksum", {11}},
9192
{"Invalid Bech32 checksum", {9, 16}},
93+
{"Bech32 string too long", {90}},
9294
};
9395
static_assert(std::size(CASES) == std::size(ERRORS), "Bech32 CASES and ERRORS should have the same length");
9496

test/functional/rpc_invalid_address_message.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_validateaddress(self):
6868
self.check_invalid(BECH32_INVALID_BECH32M, 'Version 0 witness address must use Bech32 checksum')
6969
self.check_invalid(BECH32_INVALID_VERSION, 'Invalid Bech32 address witness version')
7070
self.check_invalid(BECH32_INVALID_V0_SIZE, "Invalid Bech32 v0 address program size (21 bytes), per BIP141")
71-
self.check_invalid(BECH32_TOO_LONG, 'Bech32 string too long', list(range(90, 108)))
71+
self.check_invalid(BECH32_TOO_LONG, 'Bech32 string too long', [90])
7272
self.check_invalid(BECH32_ONE_ERROR, 'Invalid Bech32 checksum', [9])
7373
self.check_invalid(BECH32_TWO_ERRORS, 'Invalid Bech32 checksum', [22, 43])
7474
self.check_invalid(BECH32_ONE_ERROR_CAPITALS, 'Invalid Bech32 checksum', [38])

0 commit comments

Comments
 (0)