Add zeek::max_random() & fix misuse of RAND_MAX w/ zeek::random_number()

In deterministic mode, RAND_MAX is not related to the result of
zeek::random_number() (formerly bro_random()), but some logic was
using RAND_MAX as indication of the possible range of values.  The
new zeek::max_random() will give the correct upper-bound regardless
of whether deterministic-mode is used.
This commit is contained in:
Jon Siwek 2020-07-22 09:57:56 -07:00
parent bde38893ce
commit d486af06b1
6 changed files with 24 additions and 9 deletions

View file

@ -18,7 +18,7 @@ void PacketFilter::AddSrc(const IPAddr& src, uint32_t tcp_flags, double probabil
{ {
Filter* f = new Filter; Filter* f = new Filter;
f->tcp_flags = tcp_flags; f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX); f->probability = probability * static_cast<double>(zeek::max_random());
auto prev = static_cast<Filter*>(src_filter.Insert(src, 128, f)); auto prev = static_cast<Filter*>(src_filter.Insert(src, 128, f));
delete prev; delete prev;
} }
@ -27,7 +27,7 @@ void PacketFilter::AddSrc(zeek::Val* src, uint32_t tcp_flags, double probability
{ {
Filter* f = new Filter; Filter* f = new Filter;
f->tcp_flags = tcp_flags; f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX); f->probability = probability * static_cast<double>(zeek::max_random());
auto prev = static_cast<Filter*>(src_filter.Insert(src, f)); auto prev = static_cast<Filter*>(src_filter.Insert(src, f));
delete prev; delete prev;
} }
@ -36,7 +36,7 @@ void PacketFilter::AddDst(const IPAddr& dst, uint32_t tcp_flags, double probabil
{ {
Filter* f = new Filter; Filter* f = new Filter;
f->tcp_flags = tcp_flags; f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX); f->probability = probability * static_cast<double>(zeek::max_random());
auto prev = static_cast<Filter*>(dst_filter.Insert(dst, 128, f)); auto prev = static_cast<Filter*>(dst_filter.Insert(dst, 128, f));
delete prev; delete prev;
} }
@ -45,7 +45,7 @@ void PacketFilter::AddDst(zeek::Val* dst, uint32_t tcp_flags, double probability
{ {
Filter* f = new Filter; Filter* f = new Filter;
f->tcp_flags = tcp_flags; f->tcp_flags = tcp_flags;
f->probability = uint32_t(probability * RAND_MAX); f->probability = probability * static_cast<double>(zeek::max_random());
auto prev = static_cast<Filter*>(dst_filter.Insert(dst, f)); auto prev = static_cast<Filter*>(dst_filter.Insert(dst, f));
delete prev; delete prev;
} }
@ -113,5 +113,5 @@ bool PacketFilter::MatchFilter(const Filter& f, const IP_Hdr& ip,
return false; return false;
} }
return uint32_t(zeek::random_number()) < f.probability; return zeek::random_number() < f.probability;
} }

View file

@ -34,7 +34,7 @@ public:
private: private:
struct Filter { struct Filter {
uint32_t tcp_flags; uint32_t tcp_flags;
uint32_t probability; double probability;
}; };
static void DeleteFilter(void* data); static void DeleteFilter(void* data);

View file

@ -1183,13 +1183,21 @@ bool have_random_seed()
return bro_rand_determistic; return bro_rand_determistic;
} }
constexpr uint32_t zeek_prng_mod = 2147483647;
constexpr uint32_t zeek_prng_max = zeek_prng_mod - 1;
long int zeek::max_random()
{
return bro_rand_determistic ? zeek_prng_max : RAND_MAX;
}
long int zeek::prng(long int state) long int zeek::prng(long int state)
{ {
// Use our own simple linear congruence PRNG to make sure we are // Use our own simple linear congruence PRNG to make sure we are
// predictable across platforms. (Lehmer RNG, Schrage's method) // predictable across platforms. (Lehmer RNG, Schrage's method)
// Note: the choice of "long int" storage type for the state is mostly // Note: the choice of "long int" storage type for the state is mostly
// for parity with the possible return values of random(). // for parity with the possible return values of random().
constexpr uint32_t m = 2147483647; constexpr uint32_t m = zeek_prng_mod;
constexpr uint32_t a = 16807; constexpr uint32_t a = 16807;
constexpr uint32_t q = m / a; constexpr uint32_t q = m / a;
constexpr uint32_t r = m % a; constexpr uint32_t r = m % a;

View file

@ -604,7 +604,14 @@ long int prng(long int state);
* Wrapper for system random() in the default case, but when running in * Wrapper for system random() in the default case, but when running in
* deterministic mode, uses the platform-independent zeek::prng() * deterministic mode, uses the platform-independent zeek::prng()
* to obtain consistent results since implementations of rand() may vary. * to obtain consistent results since implementations of rand() may vary.
* @return A value in the range [0, zeek::max_random()].
*/ */
long int random_number(); long int random_number();
/**
* @return The maximum value that can be returned from zeek::random_number().
* When not using deterministic-mode, this is always equivalent to RAND_MAX.
*/
long int max_random();
} // namespace zeek } // namespace zeek

View file

@ -930,7 +930,7 @@ function hrw_weight%(key_digest: count, site_id: count%): count
## provided by the OS. ## provided by the OS.
function rand%(max: count%): count function rand%(max: count%): count
%{ %{
auto result = bro_uint_t(double(max) * double(zeek::random_number()) / (RAND_MAX + 1.0)); auto result = bro_uint_t(double(max) * double(zeek::random_number()) / (zeek::max_random() + 1.0));
return zeek::val_mgr->Count(result); return zeek::val_mgr->Count(result);
%} %}

View file

@ -48,7 +48,7 @@ std::string Foo::RandomString(const int len)
// zeek::random_number() is not thread-safe; as we are only using one simultaneous thread // zeek::random_number() is not thread-safe; as we are only using one simultaneous thread
// here, this should not matter in this case. If this test ever starts showing // here, this should not matter in this case. If this test ever starts showing
// random errors, this might be the culprit. // random errors, this might be the culprit.
s[i] = values[zeek::random_number() / (RAND_MAX / sizeof(values))]; s[i] = values[zeek::random_number() / (zeek::max_random() / sizeof(values))];
return s; return s;
} }