[SERVER-43643] each SecureRandom reads 8kiB out of /dev/urandom Created: 25/Sep/19  Updated: 29/Oct/23  Resolved: 02/Oct/19

Status: Closed
Project: Core Server
Component/s: Internal Code
Affects Version/s: None
Fix Version/s: 4.3.1

Type: Bug Priority: Major - P3
Reporter: Billy Donahue Assignee: Billy Donahue
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible
Operating System: ALL
Steps To Reproduce:

SECURE_RANDOM_READ_LOC=src/mongo/platform/random.cpp:127

$ gdb $BUILD_DIR/mongo/platform/platform_test
b $SECURE_RANDOM_READ_LOC
run --suite=RandomTest --filter=Secure1
(hits breakpoint)
b catch syscall
continue

Sprint: Dev Tools 2019-10-07
Participants:

 Description   

mongo::SecureRandom() holds a std::ifstream to read from /dev/urandom.
std::ifstream is buffered by default, so the first use of the SecureRandom fills that 8kiB buffer. This is extremely wasteful, as a SecureRandom object is often used for a few words and discarded, and /dev/urandom entropy and/or computational load is a resource we don't want to squander unnecessarily.

As a patch, we can reduce the buffering to maybe 64 bytes or so, possibly as part of SERVER-43641 which is in there anyway. Turning off buffering completely would incur read() syscalls on every use, which could be risky, so a 100X smaller buffer seems a good tradeoff.

https://gist.github.com/BillyDonahue/53fa229d311cc6ac1f5ab21bd588e11f#file-gistfile2-txt

Catchpoint 5 (call to syscall read), 0x00007ffff4e0534e in __libc_read (fd=6, buf=buf@entry=0x7fffeee07820, nbytes=nbytes@entry=8191) at ../sysdeps/unix/sysv/linux/read.c:27
27 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
(gdb) bt
#0 0x00007ffff4e0534e in __libc_read (fd=6, buf=buf@entry=0x7fffeee07820, nbytes=nbytes@entry=8191) at ../sysdeps/unix/sysv/linux/read.c:27
#1 0x00007ffff7916287 in std::_basic_file<char>::xsgetn (this=this@entry=0x7fffeee004d8, __s=0x7fffeee07820 '\253' <repeats 200 times>..., __n=_n@entry=8191) at basic_file.cc:285
#2 0x00007ffff793ff30 in std::basic_filebuf<char, std::char_traits<char> >::underflow (this=0x7fffeee00470) at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/bits/char_traits.h:350
#3 0x00007ffff78ee576 in std::basic_streambuf<char, std::char_traits<char> >::uflow (this=0x7fffeee00470) at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/streambuf:707
#4 std::basic_streambuf<char, std::char_traits<char> >::xsgetn (this=this@entry=0x7fffeee00470, _s=s@entry=0x7fffffffd468 " \362\333\356\377\177", __n=_n@entry=8)
at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/bits/streambuf.tcc:64
#5 0x00007ffff79409e3 in std::basic_filebuf<char, std::char_traits<char> >::xsgetn (this=0x7fffeee00470, __s=0x7fffffffd468 " \362\333\356\377\177", __n=8)
at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/bits/codecvt.h:210
#6 0x00007ffff78e989d in std::basic_streambuf<char, std::char_traits<char> >::sgetn (__n=8, __s=0x7fffffffd468 " \362\333\356\377\177", this=<optimized out>)
at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/streambuf:364
#7 std::istream::read (this=0x7fffeee00460, __s=0x7fffffffd468 " \362\333\356\377\177", __n=8) at /home/billy/dev/10gen/toolchain-builder/tmp/build-gcc-v3.sh-sRY/build/x86_64-mongodb-linux/libstdc++-v3/include/bits/istream.tcc:667
#8 0x00007ffff7c9b2be in mongo::random_detail::SecureUrbg::State::operator() (this=0x7fffeee00460) at src/mongo/platform/random.cpp:127
#9 0x00007ffff7c9b02c in mongo::random_detail::SecureUrbg::operator() (this=0x7fffeedbf220) at src/mongo/platform/random.cpp:139
#10 0x00005555557397ee in std::uniform_int_distribution<long>::operator()<mongo::random_detail::SecureUrbg> (this=0x7fffffffd5f0, __urng=..., __param=...) at /opt/mongodbtoolchain/stow/gcc-v3.sFn/include/c++/8.2.0/bits/uniform_int_dist.h:275
#11 0x0000555555736bd3 in std::uniform_int_distribution<long>::operator()<mongo::random_detail::SecureUrbg> (this=0x7fffffffd5f0, __urng=...) at /opt/mongodbtoolchain/stow/gcc-v3.sFn/include/c++/8.2.0/bits/uniform_int_dist.h:166
#12 0x0000555555733f35 in mongo::RandomBase<mongo::random_detail::SecureUrbg>::_nextAny<long> (this=0x7fffeedbf220) at src/mongo/platform/random.h:137
#13 0x000055555572f578 in mongo::RandomBase<mongo::random_detail::SecureUrbg>::nextInt64 (this=0x7fffeedbf220) at src/mongo/platform/random.h:120
#14 0x00007ffff7c7d232 in mongo::_mongoInitializerFunction_OIDGeneration (context=0x7fffffffd7b0) at src/mongo/bson/oid.cpp:58
...
#21 0x00007ffff7fe51f3 in main (argc=3, argv=0x7fffffffddb8, envp=0x7fffffffddd8) at src/mongo/unittest/unittest_main.cpp:52
#22 0x00007ffff4a24b97 in __libc_start_main (main=0x7ffff7fe519c <main(int, char*, char*)>, argc=3, argv=0x7fffffffddb8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdda8) at ../csu/libc-start.c:310
#23 0x00005555556f9cea in _start ()
(gdb)



 Comments   
Comment by Githook User [ 02/Oct/19 ]

Author:

{'name': 'Billy Donahue', 'username': 'BillyDonahue', 'email': 'billy.donahue@mongodb.com'}

Message: SERVER-43641 upgrade random.h

Respecify PseudoRandom and SecureRandom as template instances of
a `mongo::RandomBase<Urbg>` (Urbg is a UniformRandomBitGenerator).
They will only vary in which algorithm they use for their source
bits, and should otherwise support the same exact operations (e.g.
`nextCanonicalDouble`).

Fix range and stats errors in the implementations of those
RandomBase methods, and specify them in terms of the vetted
`<random>` facilities.

Test uniformity of nextInt32(max), which uses an inappropriate
( x % max) operation. Verify that refactor fixes this issue.

Just keep a shared urandom file descriptor open.

SecureRandom add fill, remove create, fix callers

Obsoletes SERVER-43643 Re: SecureRandom 8kiB buffering
Branch: master
https://github.com/mongodb/mongo/commit/96da177c6ae7b7ed0f29983ad033d8a59524b0b2

Comment by Billy Donahue [ 28/Sep/19 ]

I'm now trying what should be a very efficient approach here. It's similar to what the Python interpreter does to support its os.urandom module and its internal hash randomization. It opens one file descriptor to /dev/urandom, and retains it forever. It goes back to read() from it when necessary, but it doesn't have to open() and close() it constantly and churn or compete for file descriptors. It's working fine in PoC.

Comment by Billy Donahue [ 25/Sep/19 ]

https://mongodbcr.appspot.com/489550001/

Generated at Thu Feb 08 05:03:42 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.