streams: Fix read-past-the-end and integer overflows#24231
streams: Fix read-past-the-end and integer overflows#24231maflcko merged 3 commits intobitcoin:masterfrom
Conversation
fa0bf76 to
fa0af11
Compare
|
Pushed another commit to fix a premature throw of "end of data" on 64-bit systems, when there is still data. Unit test: diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 922fd8e513..d7a266cf1c 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -534,6 +534,21 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
} catch (const std::ios_base::failure&) {
}
+ CDataStream foo{0, 0};
+ auto size{size_t{std::numeric_limits<uint32_t>::max()} + 100};
+ foo.resize(size);
+ BOOST_CHECK_EQUAL(foo.size(), size);
+ foo.ignore(std::numeric_limits<int32_t>::max());
+ size -= std::numeric_limits<int32_t>::max();
+ BOOST_CHECK_EQUAL(foo.size(), size);
+ foo.ignore(std::numeric_limits<int32_t>::max());
+ size -= std::numeric_limits<int32_t>::max();
+ BOOST_CHECK_EQUAL(foo.size(), size);
+ BOOST_CHECK_EQUAL(foo.size(), 101);
+ foo.ignore(7); // Should work fine
+ size -= 7;
+ BOOST_CHECK_EQUAL(foo.size(), size);
+
// Very large scriptPubKey (3*10^9 bytes) past the end of the stream
CDataStream tmp(SER_DISK, CLIENT_VERSION);
uint64_t x = 3000000000ULL; |
|
mingw can't compile the bugfix: |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first. |
src/streams.h
Outdated
There was a problem hiding this comment.
Won't this truncate num_ignore to 32-bits before checking for overflows?
There was a problem hiding this comment.
Yes, but this is already done on current master. The second commit fixes this.
|
Please review #24253 first, while this stays in draft. (Can't be merged because mingw bug) |
|
Could just disable the -Werror affected, but I guess #24253 is fine too... |
…hods fa1b227 Remove broken and unused CDataStream methods (MarcoFalke) faee5f8 test: Create fresh CDataStream each time (MarcoFalke) fa71114 test: Inline expected_xor (MarcoFalke) Pull request description: The `insert` and `erase` methods have many issues: * They are unused * They are confusing and hard to read, as they implement "special cases" for optimization, that isn't needed * They are broken (See bitcoin/bitcoin#24231) * Fixing them leads to mingw compile errors (See bitcoin/bitcoin#24231 (comment)) Fix all issues by removing them ACKs for top commit: laanwj: Code review ACK fa1b227 Tree-SHA512: 9d9e5d42e6ffc5ae82bdb67cfb5b50b45977ae674acee6ff99092560aebf2fc7e4584ded614e190db0663226fa198e34350517cd7ee57d518de22e9568bc349a
-BEGIN VERIFY SCRIPT- sed -i 's/nReadPos/m_read_pos/g' ./src/streams.h -END VERIFY SCRIPT-
fa65d44 to
fa1b89a
Compare
|
Rebased. Should be trivial to re-ACK. |
|
Code Review ACK fa1b89a:
|
fa1b227 Remove broken and unused CDataStream methods (MarcoFalke) faee5f8 test: Create fresh CDataStream each time (MarcoFalke) fa71114 test: Inline expected_xor (MarcoFalke) Pull request description: The `insert` and `erase` methods have many issues: * They are unused * They are confusing and hard to read, as they implement "special cases" for optimization, that isn't needed * They are broken (See bitcoin#24231) * Fixing them leads to mingw compile errors (See bitcoin#24231 (comment)) Fix all issues by removing them ACKs for top commit: laanwj: Code review ACK fa1b227 Tree-SHA512: 9d9e5d42e6ffc5ae82bdb67cfb5b50b45977ae674acee6ff99092560aebf2fc7e4584ded614e190db0663226fa198e34350517cd7ee57d518de22e9568bc349a
…lows fa1b89a scripted-diff: Rename nReadPos to m_read_pos in streams.h (MarcoFalke) fa56c79 Make CDataStream work properly on 64-bit systems (MarcoFalke) fab02f7 streams: Fix read-past-the-end and integer overflows (MarcoFalke) Pull request description: This is a follow-up to commit e26b620 with the following fixes: * Fix unsigned integer overflow in `ignore()`, when `nReadPos` wraps. * Fix unsigned integer overflow in `read()`, when `nReadPos` wraps. * Fix read-past-the-end in `read()`, when `nReadPos` wraps. This shouldn't be remote-exploitable, because it requires a stream of more than 1GB of size. However, it might be exploitable if the attacker controls the datadir (I haven't checked). A unit test for the overflow in `ignore()` looks like following. It is left as an excercise to the reader to replace `foo.ignore(7)` with the appropriate call to `read()` to reproduce the overflow and read-error in `read()`. ```diff diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 922fd8e..ec6ea93919 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -534,6 +534,20 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) } catch (const std::ios_base::failure&) { } + CDataStream foo{0, 0}; + auto size{std::numeric_limits<uint32_t>::max()}; + foo.resize(size); + BOOST_CHECK_EQUAL(foo.size(), size); + foo.ignore(std::numeric_limits<int32_t>::max()); + size -= std::numeric_limits<int32_t>::max(); + BOOST_CHECK_EQUAL(foo.size(), size); + foo.ignore(std::numeric_limits<int32_t>::max()); + size -= std::numeric_limits<int32_t>::max(); + BOOST_CHECK_EQUAL(foo.size(), size); + BOOST_CHECK_EQUAL(foo.size(), 1); + foo.ignore(7); // Should overflow, as the size is only 1 + BOOST_CHECK_EQUAL(foo.size(), uint32_t(1 - 7)); + // Very large scriptPubKey (3*10^9 bytes) past the end of the stream CDataStream tmp(SER_DISK, CLIENT_VERSION); uint64_t x = 3000000000ULL; ``` ACKs for top commit: klementtan: Code Review ACK fa1b89a: Tree-SHA512: 67f0a1baafe88eaf1dc844ac55b638d5cf168a18c945e3bf7a2cb03c9a5976674a8e3af2487d8a2c3eae21e5c0e7a519c8b16ee7f104934442e2769d100660e9
This is a follow-up to commit e26b620 with the following fixes:
ignore(), whennReadPoswraps.read(), whennReadPoswraps.read(), whennReadPoswraps.This shouldn't be remote-exploitable, because it requires a stream of more than 1GB of size. However, it might be exploitable if the attacker controls the datadir (I haven't checked).
A unit test for the overflow in
ignore()looks like following. It is left as an excercise to the reader to replacefoo.ignore(7)with the appropriate call toread()to reproduce the overflow and read-error inread().