8000 fix initialization in FastRandomContext: removes undefined behavior & uninitialized read by martinus · Pull Request #23169 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix initialization in FastRandomContext: removes undefined behavior & uninitialized read #23169

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

martinus
Copy link
Contributor
@martinus martinus commented Oct 4, 2021

When building #23152 with g++ 11.1 a few -Wmaybe-uninitialized warnings are shown. The warning in FastRandomContext seems legit, and this PR fixes that.

Note that there are more warnings in leveldb code which I did not investigate. There are warnings in fuzz/float.cpp and fuzz/string.cpp which are also left untouched (see #23169 (comment))

@DrahtBot DrahtBot added the Tests label Oct 4, 2021
@martinus martinus force-pushed the 2021-10-fix-Wmaybe-uninitialized-warnings branch from 749e88e to 98d1d7a Compare October 4, 2021 07:51
@maflcko maflcko changed the title Fix -Wmaybe-uninitialized warnings found by building with -flto Initialize all members in FastRandomContext Oct 4, 2021
@practicalswift
Copy link
Contributor

Concept ACK on fixing the unitialized read (bitbuf) which can be tested with Valgrind using the following patch:

diff --git a/src/random.cpp b/src/random.cpp
index 174f4cef3..73e946783 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -693,13 +693,30 @@ FastRandomContext::FastRandomContext(bool fDeterministic) noexcept : requires_se
     rng.SetKey(seed.begin(), 32);
 }

+// Force Valgrind use-of-uninitialized memory (UUM) violation if `o` is uninitialized.
+//
+// As suggested by @guidovranken in https://github.com/bitcoin/bitcoin/issues/22064
+template<typename T>
+void ForceValgrindWarningIfUninitialized(const T& o) {
+    static_assert(std::is_trivially_copyable<T>::value);
+    FILE* f = fopen("/dev/null", "wb");
+    fwrite(&o, sizeof(o), 1, f);
+    fclose(f);
+}
+
 FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexcept
 {
+    ForceValgrindWarningIfUninitialized(from.requires_seed);
     requires_seed = from.requires_seed;
+    ForceValgrindWarningIfUninitialized(from.rng);
     rng = from.rng;
+    ForceValgrindWarningIfUninitialized(from.bytebuf);
     std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf));
+    ForceValgrindWarningIfUninitialized(from.bytebuf_size);
     bytebuf_size = from.bytebuf_size;
+    ForceValgrindWarningIfUninitialized(from.bitbuf);
     bitbuf = from.bitbuf;
+    ForceValgrindWarningIfUninitialized(from.bitbuf_size);
     bitbuf_size = from.bitbuf_size;
     from.requires_seed = true;
     from.bytebuf_size = 0;

And running valgrind src/test/test_bitcoin -t addrman_tests/addrman_simple:

==22209== Memcheck, a memory error detector
…
Running 1 test case...
==22209== Syscall param write(buf) points to uninitialised byte(s)
==22209==    at 0x736A264: write (write.c:27)
==22209==    by 0x72E522C: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1203)
==22209==    by 0x72E6FC0: new_do_write (fileops.c:457)
==22209==    by 0x72E6FC0: _IO_do_write@@GLIBC_2.2.5 (fileops.c:433)
==22209==    by 0x72E637F: _IO_file_close_it@@GLIBC_2.2.5 (fileops.c:136)
==22209==    by 0x72D83F6: fclose@@GLIBC_2.2.5 (iofclose.c:53)
==22209==    by 0xDA86B7: ForceValgrindWarningIfUninitialized<unsigned char [64]> (random.cpp:704)
==22209==    by 0xDA86B7: FastRandomContext::operator=(FastRandomContext&&) (random.cpp:713)
==22209==    by 0x9CFD67: Seed(FastRandomContext&) (setup_common.cpp:66)
==22209==    by 0x9D066B: SeedInsecureRand (setup_common.h:61)
==22209==    by 0x9D066B: BasicTestingSetup::BasicTestingSetup(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::vector<char const*, std::allocator<char const*> > const&) (setup_common.cpp:105)
==22209==    by 0x3AB6BE: addrman_simple (addrman_tests.cpp:166)
…

@laanwj
Copy link
Member
laanwj commented Oct 4, 2021

As it has historically been a source of some very serious vulnerabilities I'm somewhat concerned around fixing uninitialized value warnings in random functionality. Please review this carefully.
Concept ACK of course.

@maflcko
Copy link
Member
maflcko commented Oct 4, 2021

Yeah, would have been good if FastRandomContext used this from the beginning. For reference, chacha20 FastRandom was introduced in #9792

@martinus martinus force-pushed the 2021-10-fix-Wmaybe-uninitialized-warnings branch from 98d1d7a to f46225e Compare October 7, 2021 08:20
@laanwj
Copy link
Member
laanwj commented Nov 15, 2021

Code review ACK f46225e

@maflcko
Copy link
Member
maflcko commented Nov 16, 2021

I still don't understand this.

Reading #23169 (comment) tells me that the issue is (also) bytebuf, but you are fixing bitbuf and leave bytebuf an uninitialized read?

Building with `--enable-lto`in bitcoin#23152 produced a `-Wmaybe-uninitialized`
warning in FastRandomContext. This commit fixes the move constructor:
There was undefined behavior due to uninitialized copy, and
uninitialized reads in bytebuf.

The `FastRandomContext(const uint256& seed)` constructor did not
initialize the `uint64_t bitbuf` member, and in
`FastRandomContext& operator=(FastRandomContext&& from) noexcept;` the
uninitialized variable was copied with `bitbuf = from.bitbuf;` which
technically is undefined behavior. This happens in `setup_common.cpp`
in the line `ctx = FastRandomContext(seed);`.
@martinus martinus force-pushed the 2021-10-fix-Wmaybe-uninitialized-warnings branch from f46225e to 10aeb6e Compare November 18, 2021 17:34
@martinus
Copy link
Contributor Author
martinus commented Nov 18, 2021

My change was only about the undefined behavior issue, because assigning from an uninitialized variable that's not a char is undefined behavior. As far as I understand it with bytebuf that's not undefined behavior but just reading uninitialized memory: https://eel.is/c++draft/basic.indet#example-1

I've rebased and updated the PR though to get rid of reading uninitialized memory as well in 10aeb6e

@sipa
Copy link
Member
sipa commented Nov 18, 2021

If the buffer sizes are initialized, I'm pretty sure the uninitialized area of the byte buffer itself will never be read from (because it will be filled before any read happens). So I'm in favor of not initializing the byte buffer, giving analysis tools a chance to detect bugs with it.

@maflcko
Copy link
Member
maflcko commented Nov 18, 2021

Jup, I agree, see #23169 (comment).

Now that you removed the initialization, the pull request title etc should be updated? (The current patch does not change initialization)

@martinus martinus changed the title Initialize all members in FastRandomContext fix initialization in FastRandomContext: removes undefined behavior & uninitialized read Nov 19, 2021
@martinus
Copy link
Contributor Author

Right, I've updated the title

@DrahtBot
Copy link
Contributor
DrahtBot commented Sep 22, 2022

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK sipa, MarcoFalke
Concept ACK practicalswift
Stale ACK laanwj

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #26153 (Reduce wasted pseudorandom bytes in ChaCha20 + various improvements by sipa)
  • #25361 (BIP324: Cipher suite by dhruv)
  • #24748 (test/BIP324: functional tests for v2 P2P encryption by stratospher)
  • #24545 (BIP324: Enable v2 P2P encrypted transport by dhruv)
  • #23561 (BIP324: Handshake prerequisites by dhruv)
  • #23233 (BIP324: Add encrypted p2p transport {de}serializer by dhruv)

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.

@achow101
Copy link
Member

cc @jonatack @LarryRuane

@sipa
Copy link
Member
sipa commented Nov 30, 2022

utACK 10aeb6e

Copy link
Member
@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can still reproduce with gcc 12.2

Left a question.

@@ -697,9 +697,13 @@ FastRandomContext& FastRandomContext::operator=(FastRandomContext&& from) noexce
{
requires_seed = from.requires_seed;
rng = from.rng;
std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf));
if (from.bytebuf_size != 0) {
std::copy(std::begin(from.bytebuf), std::end(from.bytebuf), std::begin(bytebuf));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may still be UB if bytebuf_size is nonzero, but less than 64 and std::copy does not decay to the "simple assignment operator" (https://eel.is/c++draft/basic.indet#2.2), no?

If yes, I am not sure if it is worth it to fix, but it could be fixed by only copying the initialized part?

Otherwise, the hunk could also be dropped to leave the code in master as-is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, it should probably be std::copy(std::begin(from.bytebuf), std::begin(from.bytebuf) + from.bytebuf_size, std::begin(bytebuf));? Then the if can be dropped too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably uses = (https://eel.is/c++draft/alg.copy#3), so I guess any code is fine here. Maybe still revert to the code in master to avoid unnecessary changes and confusion?

Copy link
Member
@maflcko maflcko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to re-ACK if you decide to change something

ACK 10aeb6e 🌻

Show signature

Signature:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

ACK 10aeb6e6484048b061620d484dbb10dee0ead386 🌻
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUi1fAv/Y3d+GlbG+mm4/OqUCynfgYTZQRKI6YGchUigjXm4pKVT+zMjUZ46r+CK
4ncyI81dSp4XEroQ4Ppstljk+6tYLq/1+E6usfMQnO109oFfTqkYVLBeMdqZBUSH
w7SMoQkEmHEYxrNbx08qAy3uyymnwqkJgS7JUfVsrmdhw7CvyPBICDzGkYrtPCx2
z13VVmQPMHWjDH5700pQp9zbAXBBba+WQBQ0UfsahdPBGTABDQx86x3PtKMyW4kI
LI7LTA1s5F6VCR2W53esqdJGGsn4D03l0LBkOKU5lwXncepfiMmncisOl4y2e2ZQ
J2/TsCyk1PjqitOnHJd8SlDxOP26EqfvtjH6IHK4SE3xTvnZZegj2UDRTXH3zmis
HS4VZBRF+nYWC94Nk8zYi+PwYoreyjTZy0K9YEJcb5T1QswZeyT/vEgm3fEEe85E
a0W0k5C0T27mzcy+W3G0W0h9zot+jjsPwwLQ+HC84QqwS3FtzxlOrtCI7rs1DzgE
mkX4AlCj
=Qwgq
-----END PGP SIGNATURE-----

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

@martinus
Copy link
Contributor Author
martinus commented Mar 26, 2023

Now that #26153 was merged this is not relevant any more / was fixed (as far as I can say)

@martinus martinus closed this Mar 26, 2023
@fanquake
Copy link
Member

is not relevant any more / was fixed (as far as I can say)

There are still some -Wmaybe-uninitialized warnings produced with GCC 13.0.1 and -flto, some in random.cpp. Opened #27343 to document them + others.

@bitcoin bitcoin locked and limited conversation to collaborators Mar 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
0