-
Notifications
You must be signed in to change notification settings - Fork 37.8k
streams: Fix read-past-the-end and integer overflows #24231
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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
{ | ||
// Ignore from the beginning of the buffer | ||
if (nSize < 0) { | ||
throw std::ios_base::failure("CDataStream::ignore(): nSize negative"); | ||
auto next_read_pos{CheckedAdd<uint32_t>(nReadPos, num_ignore)}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this truncate num_ignore
to 32-bits before checking for overflows?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but this is already done on current master. The second commit fixes this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-utACK
…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()
, whennReadPos
wraps.read()
, whennReadPos
wraps.read()
, whennReadPos
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 replacefoo.ignore(7)
with the appropriate call toread()
to reproduce the overflow and read-error inread()
.