Skip to content

Conversation

@jeking3
Copy link
Collaborator

@jeking3 jeking3 commented Nov 8, 2017

Changed the default random_generator implementation to use
operating-system provided entropy as it is more secure and
faster for the typical use case of generating one uuid at
a time.

This is a breaking change for anyone passing a mt19937
into one of the explicit constructors of random_generator,
which would be quite rare.

Changed the default random provider on Windows to use BCrypt
where available, falling back to Wincrypt when necessary or
when explicitly requested through a macro.

Provide a new random_generator_mt19937 type definition for
use cases where a large number of uuids need to be created
with high performance. This is equivalent to the previous
definition of random_generator.

Provide a random generation benchmark test showing the
cutoff where the mt19937-based generator will outperform the
standard generator based on wall time.

Removed template specialization for boost::random::random_device
so that any UniformRandomNumberGenerator can be used properly
with random_generator.

Replaced the seed_rng detail implementation (which had a number
of flaws) with a replacement header-only random_provider
implementation.

Note: entropy generation errors will cause an entropy_error
to be thrown from random_generator. The previous implementation
ignored errors and silently failed.

Added internal support for entropy generation on cloudabi
platform leveraging the new random_device implementation.

Added internal support for Universal Windows Platform (UWP)
development leveraging the new random_device implementation.

Added internal support for getentropy() on Linux and OpenBSD
if certain requirements are met.

This is an evolution of #52
This fixes #24

@jeking3 jeking3 added this to the v1.67.0 milestone Nov 8, 2017
@jeking3 jeking3 self-assigned this Nov 8, 2017
@jeking3 jeking3 requested a review from pdimov November 8, 2017 21:42
@codecov
Copy link

codecov bot commented Nov 8, 2017

Codecov Report

Merging #53 into develop will decrease coverage by 0.21%.
The diff coverage is 98.14%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop      #53      +/-   ##
===========================================
- Coverage    97.41%   97.19%   -0.22%     
===========================================
  Files           11       13       +2     
  Lines          619      607      -12     
===========================================
- Hits           603      590      -13     
- Misses          16       17       +1
Impacted Files Coverage Δ
include/boost/uuid/basic_name_generator.hpp 100% <ø> (ø) ⬆️
include/boost/uuid/random_generator.hpp 100% <100%> (ø) ⬆️
include/boost/uuid/entropy_error.hpp 100% <100%> (ø)
include/boost/uuid/detail/random_provider.hpp 100% <100%> (ø)
...nclude/boost/uuid/detail/random_provider_posix.ipp 96% <96%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5a48cab...b27755e. Read the comment docs.

template<class Iter>
void generate(Iter first, Iter last)
{
BOOST_STATIC_ASSERT(sizeof(Iter) >= sizeof(unsigned int));
Copy link
Member

Choose a reason for hiding this comment

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

This checks the iterator size, which is irrelevant. It should check

  • whether the value_type of the iterator is integral;
  • whether the value_type of the iterator is unsigned;
  • whether numeric_limits<value_type>::max() is >= 2^32-1, but this can only be tested at compile time on C++11, so
  • whether sizeof(value_type) * CHAR_BIT >= 32.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, I will update this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I tried this but it didn't work - I'll keep on it.

        typedef Iter::value_type iter_value_type;
        BOOST_STATIC_ASSERT(is_integral<iter_value_type>::value == true_type);
        BOOST_STATIC_ASSERT(is_unsigned<iter_value_type>::value == true_type);
        BOOST_STATIC_ASSERT(sizeof(iter_value_type) * CHAR_BIT >= 32);

In reading SeedSeq documentation, Iter can be a class with a value_type type definition of an unsigned integral and an operator*, or it can be a pointer to an unsigned integral, correct? I'm not certain (yet) how I would test for both.

Copy link
Member

Choose a reason for hiding this comment

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

typedef std::iterator_traits<Iter>::value_type value_type;

Copy link
Member

Choose a reason for hiding this comment

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

Then just BOOST_STATIC_ASSERT( boost::is_integral<value_type>::value );, comparing to the type true_type doesn't make sense.

Copy link
Member

Choose a reason for hiding this comment

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

It will work in C++11 because true_type has a constexpr conversion to true but is still nonsensical. :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I want to support C++03, will it still work?
I don't think I have ever used std::iterator_traits before.
I'm learning a ton of new tricks, thanks!

for (; first != last; ++first)
{
get_random_bytes(&*first, sizeof(*first));
*first &= (std::numeric_limits<unsigned int>::max)();
Copy link
Member

Choose a reason for hiding this comment

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

Not correct. generate is required to produce numbers in [0..2^32-1], regardless of the size of unsigned int.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Got it, I will update this.

#include <boost/uuid/entropy_error.hpp>

#include <boost/uuid/detail/random_provider_detect_platform.hpp>
#include BOOST_UUID_RANDOM_PROVIDER_BASE_INCLUDE()
Copy link
Member

Choose a reason for hiding this comment

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

It's better to just include the appropriate platform header from random_provider_detect_platform.hpp, renaming it into random_provider_base.hpp in the process, because the above confuses dependency scanners; they can't see through the macro.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I needed platform detection separate from loading the implementation to make the mocking work properly. I can expand that out so the macro isn't there and put additional preprocessor branches based on the BOOST_UUID_RANDOM_PROVIDER_* definitions made by the detection logic in that header.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's better to spell it out with #ifdefs instead of a macro include.

doc/uuid.html Outdated
<ul>
<li>
<tt>BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX</tt>: on Unix this will force the
selection of <tt>/dev/*random</tt> over getentropy(3).
Copy link
Member

Choose a reason for hiding this comment

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

Prefer to not provide this. By all means give people a macro to override behavior when they ask for it, no need to to it preemptively.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm leveraging this to get coverage and a (mostly) common sad path test. I'll look at testing against the posix.ipp implementation directly when running on linux.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I hadn't yet gotten to the testing part. Keep those macros. I was looking at them from the user side.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That'll save a bunch of additional work... thanks.

</li>
<li>
<tt>BOOST_UUID_RANDOM_PROVIDER_FORCE_WINCRYPT</tt>: on Windows this will force the
selection of Wincrypt over BCrypt.
Copy link
Member

Choose a reason for hiding this comment

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

Ditto. No need to ever prefer the older API. If someone asks for it, fine; until then, no need.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm leveraging this to get coverage and a (mostly) common sad path test. This test is more complex as I have to build a DLL and link against it, but I can make another test file that's specific to the wincrypt.ipp file and loads the mock library.

doc/uuid.html Outdated
</li>
<li>
<tt>BOOST_UUID_RANDOM_PROVIDER_POSIX_BLOCKING</tt>: when using <tt>/dev/*random</tt>,
if this is defined, skip attempting to open <tt>/dev/urandom</tt> first.
Copy link
Member

Choose a reason for hiding this comment

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

Nope. Not only should /dev/random not be configurable to be used as a preference, it should never be used, anywhere, for anything, as the OpenBSD docs say.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Easy to remove. Thanks. Less options too and it simplifies the code and the sad test path that has to account for two open()s in the failure case.

boost::winapi::NTSTATUS_ status =
boost::winapi::BCryptGenRandom(
hProv_,
reinterpret_cast<boost::winapi::PUCHAR_>(buf),
Copy link
Member

Choose a reason for hiding this comment

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

static_cast is better, when it works, which it does here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree, I'll change it. I usually try static_cast first and let the compiler force me the other way.


#include <boost/predef/library/c/gnu.h>
#include <boost/predef/platform/cloudabi.h>
// #include <boost/predef/os/bsd/open.h> re-enable when Boost.Predef #66 clears
Copy link
Member

Choose a reason for hiding this comment

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

No need to wait for Predef. The functions we need are available on OpenBSD 2.5 and above.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good to know, I was planning on getentropy() there so... (on to the next comment)

#elif BOOST_LIB_C_GNU >= BOOST_VERSION_NUMBER(2, 25, 0) \
&& !defined(BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX)
/* when Boost.Prefef #6 merges, allow this based on OpenBSD documentation:
BOOST_OS_BSD_OPEN >= BOOST_VERSION_NUMBER(5, 6, 0)) */
Copy link
Member

Choose a reason for hiding this comment

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

"getentropy() is not intended for regular code; please use the arc4random(3) family of functions instead."

https://man.openbsd.org/getentropy.2

Heed what the OpenBSD people say. They know their stuff. :-)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

and there you go.

#include <boost/throw_exception.hpp>
#include <string>

#if !defined(BOOST_UUID_RANDOM_PROVIDER_NO_LIB)
Copy link
Member

Choose a reason for hiding this comment

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

BOOST_ALL_NO_LIB, as I already said.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry I missed that! Isn't each library capable of having its own preprocessor macros in this area, like BOOST_SYSTEM_NO_LIB, BOOST_CONFIG_NO_LIB, BOOST_PYTHON_NO_LIB...?

Copy link
Member

Choose a reason for hiding this comment

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

It is, but you have to check the global macro as well and not autolink if it's set. It's a global user switch that requests all autolinking to be disabled, anywhere.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

In my builds on Windows with msvc-14.1, BOOST_ALL_NO_LIB is set to 1 by default. This seems odd - why would it be set on the way in? Boost.Uuid does not set it.

Copy link
Member

Choose a reason for hiding this comment

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

Interesting. No idea why it's set; you'll need to investigate.

Copy link
Collaborator Author

@jeking3 jeking3 Nov 9, 2017

Choose a reason for hiding this comment

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

It is defined for all builds of boost, it's in boost.jam. I will add BOOST_UUID_SOURCE and make it conditional on that as well. This pattern is found elsewhere.

#if defined(BOOST_NO_ANSI_APIS)
boost::winapi::CryptAcquireContextW
#else
boost::winapi::CryptAcquireContextA
Copy link
Member

Choose a reason for hiding this comment

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

No reason to ever use the A function here, if you ask me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe you may be right now that the provider string / enumeration stuff is gone. If so I'll simplify and remove the additional test for this macro.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

Ah. It now occurs to me that you need the configuration macros in order to force-test a provider when it wouldn't be selected otherwise. Makes sense.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 8, 2017

That's what I was using them for, however I could write one test per provider implementation instead I suppose. I just wanted it to be as uniform as possible since they all follow the same pattern.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

Yes, you could include the implementation header directly and test that, which is still not a bad idea, but the other way is good too, as it can test the component integrated into the rest of the code. So, either way is fine with me, I suppose.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 8, 2017

As an aside, we need to get this level of CI integration into every project.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

It still seems to me that RtlGenRandom is quite a convenient choice on Windows as it requires no setup and is quite likely a bit faster than firing up a bcrypt/wincrypt provider. I'm not entirely clear on why you dislike it so. :-)

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 8, 2017

I believe it would still have a setup cost insofar as needing to perhaps statically initialize the address of the API call through LoadLibrary and GetProcAddress, and in a previous PR I was told to avoid using statics. I agree those two calls are probably much lighter than wincrypt. I can give it a try.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

There's no need for LoadLibrary.

extern "C" unsigned char __stdcall SystemFunction036( void * p, unsigned long n );

works for me.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 8, 2017

You are assuming that advapi32.dll has already been loaded, and I think that's an unsafe assumption to make.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

It works for me in a sample program. Not sure how I could be assuming anything if the program links.

@pdimov
Copy link
Member

pdimov commented Nov 8, 2017

extern "C" unsigned char __stdcall SystemFunction036( void * p, unsigned long n );

bool RtlGenRandom( void * p, unsigned long n )
{
	return SystemFunction036( p, n );
}

#include <iostream>

int main()
{
	unsigned x = 0;
	bool r = RtlGenRandom( &x, sizeof(x) );

	std::cout << x << std::endl;
}
C:\Projects\testbed>g++ testbed.cpp

C:\Projects\testbed>a
725953787

C:\Projects\testbed>clang++ testbed.cpp

C:\Projects\testbed>a
1701069008

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 9, 2017

You are still relying on the fact that ADVAPI32.DLL was loaded. While it is in the default library list for most build systems, that can be overridden. ADVAPI32.DLL is not available on Windows CE. Pretty much everyone will be using bcrypt at this point with wincrypt left to cover older platforms. Since that code has worked for a very long time here and in Boost.Random, and seeing as to how it's out of the normal code path, doesn't it make sense to leave it as is?

@Lastique
Copy link
Member

Lastique commented Nov 9, 2017

@pdimov The LoadLibrary/GetProcAddress approach is what MS suggests here. All in all, this function seems like one of those semi-legal half-documented APIs that are Windows implementation details and can change at any time. IMHO, we can use such APIs but only if we have to. If there is an official public API that covers our case, we should preferably use it.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 9, 2017

Okay that actually wasn't too bad, I'm doing some testing...

@pdimov
Copy link
Member

pdimov commented Nov 9, 2017

It's impossible for a library to not be loaded if you link to it. If that's unsafe, your code is at least 20 times as unsafe.

, distribution_type
( (std::numeric_limits<unsigned long>::min)()
, (std::numeric_limits<unsigned long>::max)()
( (std::numeric_limits<unsigned int>::min)()
Copy link
Member

Choose a reason for hiding this comment

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

Not clear why these changes from ulong to uint are needed. Would probably be better to keep the original to cut down on the delta unless there's a good reason to switch.

test/Jamfile.v2 Outdated
<define>BOOST_UUID_SOURCE=1

# BOOST_AUTO_LINK does not work outside of MSVC on Windows so this makes cygwin, mingw-w64 work:
<toolset>gcc,<target-os>windows:<linkflags>-lbcrypt
Copy link
Member

Choose a reason for hiding this comment

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

We could link here regardless of whether the toolset is gcc (there's also clang btw), and this will fix the lack of autolink when using Boost.Build. But the problem is that we don't necessarily know what to link to.

<linkflags>-lbcrypt isn't correct though, I think that we need lib bcrypt ; and then <library>bcrypt here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I will try that and see what it does to the mocks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Is there any way to negate this (<target-os>windows:<library>bcrypt), so that I can set it in project requirements so everything links against it by default, but then in a specific mock based test negate it so it does NOT link against bcrypt?

Copy link
Member

Choose a reason for hiding this comment

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

I vaguely recall that one could use <x>y:-<library>bcrypt to remove it, but I won't be surprised if it doesn't work and I'm imagining things. :-)

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Odd, test_serialization passed the second time. Mystery.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of not testing anything, it might be better to disable just test_serialization on mingw and keep testing the rest.

Copy link
Member

Choose a reason for hiding this comment

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

Cygwin, on the other hand, passes with flying colors.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am unable to use the syntax: <target-os>windows:-<library>bcrypt, or <target-os>windows:-<define>BOOST_ALL_NO_LIB - both fail in bjam as an invalid target-os. I'm going back to the logic I had.

Copy link
Member

Choose a reason for hiding this comment

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

If only there existed a simple function in advapi32 that we could just call and avoid all that hassle.

test/Jamfile.v2 Outdated
[ run test_detail_random_provider.cpp
: : :
<define>BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX # will force POSIX over getentropy
<define>BOOST_UUID_RANDOM_PROVIDER_POSIX_BLOCKING # will force /dev/random over /dev/urandom
Copy link
Member

Choose a reason for hiding this comment

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

No longer necessary.


entropy_error err(6, "foo");
BOOST_TEST_EQ(6, err.errcode());
BOOST_TEST_EQ(std::string("foo"), std::string(err.what()));
Copy link
Member

Choose a reason for hiding this comment

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

Could use BOOST_TEST_CSTR_EQ here.

@swatanabe
Copy link
Contributor

swatanabe commented Nov 9, 2017 via email

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 9, 2017

I have some changes to support building for CloudABI compatibility since it's in this pull request, now that I was able to build the Boost.Uuid tests under CloudABI. These depend on boostorg/predef#68 so I won't push them until that merges.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 9, 2017

One of the developers from CloudABI pointed me to arc4random in cloudlibc so I'm going to remove the cloudabi specific implementation and have it use arc4random.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 9, 2017

I'm curious what you think about the scope of this change relative to the upcoming release. I have it marked as a milestone for 1.67.0 and not 1.66.0 right now because it is a lot of change, however it's well tested on every platform, both happy and sad paths.

@pdimov
Copy link
Member

pdimov commented Nov 9, 2017

1.66.0 is closed for beta now so I'd say we missed that boat.

appveyor.yml Outdated
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
ADDPATH: C:\mingw\bin;
TOOLSET: gcc
CXXFLAGS: cxxflags=-std=c++03
Copy link
Member

Choose a reason for hiding this comment

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

The new way is cxxstd=03 instead of using cxxflags, which allows testing cxxstd=03,11 or even cxxstd=03,11,14,1z in one go instead of using multiple jobs (slow on Appveyor as they're serialized).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, I just re-enabled an old stanza here. The other ones should be using that. If not, I'll adjust.

Copy link
Member

Choose a reason for hiding this comment

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

All of the jobs at https://ci.appveyor.com/project/jeking3/uuid-gaamf/build/1.0.73-develop seem to be using cxxflags.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I was thinking about Travis CI, I'll get this fixed; it's queued up behind boostorg/predef#68 right now.

# define BOOST_LIB_NAME "advapi32"
# endif
# define BOOST_AUTO_LINK_NOMANGLE
# include <boost/config/auto_link.hpp>
Copy link
Member

Choose a reason for hiding this comment

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

I've a strong suspicion that these autolinks are unnecessary. Can't imagine "coredll" not being linked on CE, and "advapi32" is on desktop.

Copy link
Member

Choose a reason for hiding this comment

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

Hm, nope, a plain cl doesn't link advapi32 by default, so we do need it, unless Winapi autolinks it for us.

Copy link
Member

Choose a reason for hiding this comment

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

Interestingly, Cygwin/Mingw g++/clang++ do autolink advapi32 though.

@pdimov
Copy link
Member

pdimov commented Nov 9, 2017

This looks good. I however still think that it's way more complex than it would have been with RtlGenRandom on desktop. It's even available by default on Cygwin/Mingw, so things automatically work there even though autolink's not available.

operating-system provided entropy as it is more secure and
faster for the typical use case of generating one uuid at
a time.

This is a breaking change for anyone passing a mt19937
into one of the explicit constructors of random_generator,
which would be quite rare.

Changed the default random provider on Windows to use BCrypt
where available, falling back to Wincrypt when necessary or
when explicitly requested through a macro.

Provide a new random_generator_mt19937 type definition for
use cases where a large number of uuids need to be created
with high performance.  This is equivalent to the previous
definition of random_generator.

Provide a random generation benchmark test showing the
cutoff where the mt19937-based generator will outperform the
standard generator based on wall time.

Removed template specialization for boost::random::random_device
so that any UniformRandomNumberGenerator can be used properly
with random_generator.

Replaced the seed_rng detail implementation (which had a number
of flaws) with a replacement header-only random_provider
implementation.

Note: entropy generation errors will cause an entropy_error
to be thrown from random_generator.  The previous implementation
ignored errors and silently failed.

Added internal support for entropy generation on cloudabi
platform leveraging the new random_device implementation.

Added internal support for Universal Windows Platform (UWP)
development leveraging the new random_device implementation.

Added internal support for getentropy() on Linux and OpenBSD
if certain requirements are met.
@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 10, 2017

This (hopyfully last) refresh adds changes to support actually running the test suite on cloudabi in a FreeBSD VM. I switched CloudABI to using arc4random as well.

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 11, 2017

@swatanabe with one more turn of the crank I think this would be something useful to consider moving into Boost.Random as a replacement for the existing random_device implementation. Given that the random_device inside Boost.Random today is the only piece of code referencing Boost.System, if we did this then Boost.Random would no longer depend on Boost.System. I don't know if that would free up Boost.Random from all library based dependencies though.

A fairly simple random_device implementation based on this would be (incomplete...):

jking@ubuntu:~/boost/libs/uuid/test$ cat ../include/boost/uuid/detail/random_device.hpp 
#include <boost/uuid/detail/random_provider.hpp>

namespace boost {
namespace uuids {
namespace detail {

template<class Entropy>
class random_device : public random_provider
{
public:
    Entropy operator()()
    {
        Entropy e;
        get_random_bytes(&e, sizeof(Entropy));
        return e;
    }
};
}}}


jking@ubuntu:~/boost/libs/uuid/test$ git diff HEAD test_random_generator.cpp
diff --git a/test/test_random_generator.cpp b/test/test_random_generator.cpp
index bd9566d..6d4e4a0 100644
--- a/test/test_random_generator.cpp
+++ b/test/test_random_generator.cpp
@@ -13,6 +13,7 @@
 #include <boost/detail/lightweight_test.hpp>
 #include <boost/predef/library/c/cloudabi.h>
 #include <boost/random.hpp>
+#include <boost/uuid/detail/random_device.hpp>
 #include <boost/uuid/entropy_error.hpp>
 #include <boost/uuid/random_generator.hpp>
 #include <boost/uuid/uuid_io.hpp>
@@ -117,5 +118,10 @@ int main(int, char*[])
     }
 #endif
 
+    boost::uuids::detail::random_device<unsigned int> randgen;
+    unsigned int i1 = randgen();
+    unsigned int i2 = randgen();
+    BOOST_TEST_NE(i1, i2);
+
     return boost::report_errors();
 }

This would improve the platform coverage of random_device to include UWP and CloudABI, and use more optimal calls on newer versions of Linux. The implementation is also fully tested (happy and sad paths), although the OpenBSD and CloudABI paths are tested manually unless I turn the build into a docker based build. (I have extensive work doing in the Apache Thrift project, so I know this would be possible).

@jeking3
Copy link
Collaborator Author

jeking3 commented Nov 15, 2017

Should I complete the implementation as a random_device replacement and submit it to Boost.Random?

@jeking3
Copy link
Collaborator Author

jeking3 commented Dec 5, 2017

Now that 1.66.0 is closing out, I want to revisit this, but I need an answer, so please reply:

Should I complete the implementation as a random_device replacement and submit it to Boost.Random instead?

The benefits are:

  • Broader platform support for entropy generation
  • More optimal entropy generation leveraging the latest APIs
  • Lower maintenance cost
  • Reduced library dependencies (by decoupling random from system)
  • Comprehensive happy and sad path testing with every build (to the extent current CI allows)

Thanks - Jim

@Lastique
Copy link
Member

Lastique commented Dec 5, 2017

I thought we've been over this and Boost.Random submission failed, haven't we?

My opinion has not changed. You can do it however you like in implementation details of Boost.UUID. A fully compliant implementation of random_device in Boost.UUID details seem like an overkill to me; the functionality needed for Boost.UUID can be implemented much more simply. Depending on Boost.Random is only ok if it doesn't require linking with it (which basically means using random_device from Boost.Random is not an option).

@pdimov
Copy link
Member

pdimov commented Dec 5, 2017

The right place to ask this question is the list.

@jeking3
Copy link
Collaborator Author

jeking3 commented Dec 7, 2017

I submitted a PR into Boost.Random: boostorg/random#34 - I hadn't heard back from Steven and didn't want to drag the issue up on the mailing list again.

@jeking3 jeking3 closed this in a8a1ec3 Dec 18, 2017
@jeking3 jeking3 deleted the random_v2 branch April 29, 2018 13:59
@jgzhuang
Copy link

jgzhuang commented Jun 2, 2018

@jeking3 Would you mind sharing the details of the "flaws" you mentioned in "Replaced the seed_rng detail implementation (which had a number of flaws) with a replacement header-only random_provider implementation."?
I did notice a few strange things in the seed_rng in 1.66 and earlier (such as using the uninitialized content, new unsigned int and use the address and delete that unsigned int). But I am interested in the flaws you discovered.

Another implementation difference between seed_rng in 1.66 and the random_provider_base in 1.67 is that seed_rng uses std::fread while the random_provider_base uses ::read. With a synthetic testing it seems the latter is slower when siz is 4 although this only affects seeding.

@jeking3
Copy link
Collaborator Author

jeking3 commented Jun 2, 2018

In particular the code was:

  • relying on uninitialized variables
  • did not check for errors while obtaining entropy
  • had become difficult to maintain because the entropy generation was not well separated per platform.
  • was doing way too much to try and be random, reading in process ID, thread ID, time of day, etc. and then running it through a sha1 hash, then pulling some bits. Much better to use the operating system's entropy generator which was the primary purpose of the change, and to add more platform support.

If you have evidence that shows ::read is slower than std::fread then I suggest opening an issue or a pull request with the evidence and the fix.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

seed_rng.hpp does not compile for Windows UWP apps

5 participants