From: bmeurer@chromium.org Date: Tue, 10 Sep 2013 11:13:55 +0000 (+0000) Subject: Introduce a RandonNumberGenerator class. Refactor the random/private_random uses... X-Git-Tag: upstream/4.7.83~12594 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=eb381b9444c6b1ec78414d1c9375f67c320cae74;p=platform%2Fupstream%2Fv8.git Introduce a RandonNumberGenerator class. Refactor the random/private_random uses in Isolate/Context. The RandomNumberGenerator is a pseudorandom number generator with 48-bit state. It is properly seeded using either (1) the --random-seed if specified, or (2) the entropy_source function if configured, or (3) /dev/urandom if available, or (4) falls back to Time and TimeTicks based seeding. Each Isolate now contains a RandomNumberGenerator, which replaces the previous private_random_seed. Every native context still has its own random_seed. But this random seed is now properly initialized during bootstrapping, instead of on-demand initialization. This will allow us to cleanup and speedup the HRandom implementation quite a lot (this is delayed for a followup CL)! Also stop messing with the system rand()/random(), which should not be done from a library anyway! We probably re-seeded the libc rand()/random() after the application (i.e. Chrome) already seeded it (with better entropy than what we used). Another followup CL will replace the use of the per-isolate random number generator for the address randomization and thereby get rid of the Isolate::UncheckedCurrent() usage in the platform code. TEST=cctest/test-random-number-generator,cctest/test-random R=mstarzinger@chromium.org Review URL: https://codereview.chromium.org/23548024 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16612 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/api.cc b/src/api.cc index bf27217..d1f6b78 100644 --- a/src/api.cc +++ b/src/api.cc @@ -62,6 +62,7 @@ #include "scanner-character-streams.h" #include "snapshot.h" #include "unicode-inl.h" +#include "utils/random-number-generator.h" #include "v8threads.h" #include "version.h" #include "vm-state-inl.h" @@ -5208,8 +5209,8 @@ bool v8::V8::Initialize() { } -void v8::V8::SetEntropySource(EntropySource source) { - i::V8::SetEntropySource(source); +void v8::V8::SetEntropySource(EntropySource entropy_source) { + i::RandomNumberGenerator::SetEntropySource(entropy_source); } diff --git a/src/assembler.cc b/src/assembler.cc index 44d5f22..fbff62d 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -43,7 +43,7 @@ #include "deoptimizer.h" #include "execution.h" #include "ic.h" -#include "isolate.h" +#include "isolate-inl.h" #include "jsregexp.h" #include "lazy-instance.h" #include "platform.h" @@ -119,7 +119,7 @@ AssemblerBase::AssemblerBase(Isolate* isolate, void* buffer, int buffer_size) emit_debug_code_(FLAG_debug_code), predictable_code_size_(false) { if (FLAG_mask_constants_with_cookie && isolate != NULL) { - jit_cookie_ = V8::RandomPrivate(isolate); + jit_cookie_ = isolate->random_number_generator()->NextInt(); } if (buffer == NULL) { diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 92ff269..764c846 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -1316,13 +1316,9 @@ void Genesis::InitializeGlobal(Handle inner_global, Handle embedder_data = factory->NewFixedArray(2); native_context()->set_embedder_data(*embedder_data); - { - // Initialize the random seed slot. - Handle zeroed_byte_array( - factory->NewByteArray(kRandomStateSize)); - native_context()->set_random_seed(*zeroed_byte_array); - memset(zeroed_byte_array->GetDataStartAddress(), 0, kRandomStateSize); - } + // Allocate the random seed slot. + Handle random_seed = factory->NewByteArray(kRandomStateSize); + native_context()->set_random_seed(*random_seed); } @@ -2647,6 +2643,14 @@ Genesis::Genesis(Isolate* isolate, InitializeExperimentalGlobal(); if (!InstallExperimentalNatives()) return; + // Initially seed the per-context random number generator + // using the per-isolate random number generator. + uint32_t* state = reinterpret_cast( + native_context()->random_seed()->GetDataStartAddress()); + do { + isolate->random_number_generator()->NextBytes(state, kRandomStateSize); + } while (state[0] == 0 || state[1] == 0); + result_ = native_context(); } diff --git a/src/flags.h b/src/flags.h index f0b239b..fe182e5 100644 --- a/src/flags.h +++ b/src/flags.h @@ -24,9 +24,12 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #ifndef V8_FLAGS_H_ #define V8_FLAGS_H_ +#include "atomicops.h" + namespace v8 { namespace internal { diff --git a/src/heap.cc b/src/heap.cc index aaa3d8b..c06a592 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -38,6 +38,7 @@ #include "global-handles.h" #include "heap-profiler.h" #include "incremental-marking.h" +#include "isolate-inl.h" #include "mark-compact.h" #include "natives.h" #include "objects-visiting.h" @@ -47,6 +48,7 @@ #include "scopeinfo.h" #include "snapshot.h" #include "store-buffer.h" +#include "utils/random-number-generator.h" #include "v8threads.h" #include "v8utils.h" #include "vm-state-inl.h" @@ -5762,7 +5764,7 @@ MaybeObject* Heap::AllocateSymbol() { int hash; int attempts = 0; do { - hash = V8::RandomPrivate(isolate()) & Name::kHashBitMask; + hash = isolate()->random_number_generator()->NextInt() & Name::kHashBitMask; attempts++; } while (hash == 0 && attempts < 30); if (hash == 0) hash = 1; // never return 0 @@ -6927,8 +6929,8 @@ bool Heap::SetUp() { ASSERT(hash_seed() == 0); if (FLAG_randomize_hashes) { if (FLAG_hash_seed == 0) { - set_hash_seed( - Smi::FromInt(V8::RandomPrivate(isolate()) & 0x3fffffff)); + int rnd = isolate()->random_number_generator()->NextInt(); + set_hash_seed(Smi::FromInt(rnd & Name::kHashBitMask)); } else { set_hash_seed(Smi::FromInt(FLAG_hash_seed)); } diff --git a/src/isolate-inl.h b/src/isolate-inl.h index 5e2c8c2..45076f5 100644 --- a/src/isolate-inl.h +++ b/src/isolate-inl.h @@ -28,9 +28,9 @@ #ifndef V8_ISOLATE_INL_H_ #define V8_ISOLATE_INL_H_ -#include "isolate.h" - #include "debug.h" +#include "isolate.h" +#include "utils/random-number-generator.h" namespace v8 { namespace internal { @@ -67,6 +67,13 @@ bool Isolate::DebuggerHasBreakPoints() { } +RandomNumberGenerator* Isolate::random_number_generator() { + if (random_number_generator_ == NULL) { + random_number_generator_ = new RandomNumberGenerator; + } + return random_number_generator_; +} + } } // namespace v8::internal #endif // V8_ISOLATE_INL_H_ diff --git a/src/isolate.cc b/src/isolate.cc index 74a77c6..3a2084a 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -39,7 +39,7 @@ #include "deoptimizer.h" #include "heap-profiler.h" #include "hydrogen.h" -#include "isolate.h" +#include "isolate-inl.h" #include "lithium-allocator.h" #include "log.h" #include "marking-thread.h" @@ -54,6 +54,7 @@ #include "spaces.h" #include "stub-cache.h" #include "sweeper-thread.h" +#include "utils/random-number-generator.h" #include "version.h" #include "vm-state-inl.h" @@ -1772,6 +1773,9 @@ Isolate::Isolate() regexp_stack_(NULL), date_cache_(NULL), code_stub_interface_descriptors_(NULL), + // TODO(bmeurer) Initialized lazily because it depends on flags; can + // be fixed once the default isolate cleanup is done. + random_number_generator_(NULL), has_fatal_error_(false), use_crankshaft_(true), initialized_from_snapshot_(false), @@ -2047,6 +2051,9 @@ Isolate::~Isolate() { delete external_reference_table_; external_reference_table_ = NULL; + delete random_number_generator_; + random_number_generator_ = NULL; + #ifdef ENABLE_DEBUGGER_SUPPORT delete debugger_; debugger_ = NULL; diff --git a/src/isolate.h b/src/isolate.h index 1debf4b..8db6f4b 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -77,6 +77,7 @@ class NoAllocationStringAllocator; class InnerPointerToCodeCache; class MarkingThread; class PreallocatedMemoryThread; +class RandomNumberGenerator; class RegExpStack; class SaveContext; class UnicodeCache; @@ -1125,6 +1126,8 @@ class Isolate { void* stress_deopt_count_address() { return &stress_deopt_count_; } + inline RandomNumberGenerator* random_number_generator(); + // Given an address occupied by a live code object, return that object. Object* FindCodeObject(Address a); @@ -1306,6 +1309,7 @@ class Isolate { DateCache* date_cache_; unibrow::Mapping interp_canonicalize_mapping_; CodeStubInterfaceDescriptor* code_stub_interface_descriptors_; + RandomNumberGenerator* random_number_generator_; bool is_memory_constrained_; // True if fatal error has been signaled for this isolate. diff --git a/src/objects.cc b/src/objects.cc index 507fbfc..6476871 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4719,7 +4719,7 @@ Smi* JSReceiver::GenerateIdentityHash() { do { // Generate a random 32-bit hash value but limit range to fit // within a smi. - hash_value = V8::RandomPrivate(isolate) & Smi::kMaxValue; + hash_value = isolate->random_number_generator()->NextInt() & Smi::kMaxValue; attempts++; } while (hash_value == 0 && attempts < 30); hash_value = hash_value != 0 ? hash_value : 1; // never return 0 diff --git a/src/platform-cygwin.cc b/src/platform-cygwin.cc index 6b41417..d7dcaf0 100644 --- a/src/platform-cygwin.cc +++ b/src/platform-cygwin.cc @@ -236,8 +236,9 @@ static void* GetRandomAddr() { static const intptr_t kAllocationRandomAddressMin = 0x04000000; static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000; #endif - uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits) - | kAllocationRandomAddressMin; + uintptr_t address = + (isolate->random_number_generator()->NextInt() << kPageSizeBits) | + kAllocationRandomAddressMin; address &= kAllocationRandomAddressMax; return reinterpret_cast(address); } @@ -366,16 +367,4 @@ bool VirtualMemory::HasLazyCommits() { return false; } - -void OS::SetUp() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly can cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast(TimeCurrentMillis()); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index b21ebf7..007bc0d 100644 --- a/src/platform-freebsd.cc +++ b/src/platform-freebsd.cc @@ -340,16 +340,4 @@ bool VirtualMemory::HasLazyCommits() { return false; } - -void OS::SetUp() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly can cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast(TimeCurrentMillis()); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-linux.cc b/src/platform-linux.cc index 30825d7..d0fd77a 100644 --- a/src/platform-linux.cc +++ b/src/platform-linux.cc @@ -466,12 +466,4 @@ bool VirtualMemory::HasLazyCommits() { return true; } - -void OS::SetUp() { - // Seed the random number generator. We preserve microsecond resolution. - uint64_t seed = static_cast(TimeCurrentMillis()) ^ (getpid() << 16); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-macos.cc b/src/platform-macos.cc index 5dd38b2..f618737 100644 --- a/src/platform-macos.cc +++ b/src/platform-macos.cc @@ -360,12 +360,4 @@ bool VirtualMemory::HasLazyCommits() { return false; } - -void OS::SetUp() { - // Seed the random number generator. We preserve microsecond resolution. - uint64_t seed = static_cast(TimeCurrentMillis()) ^ (getpid() << 16); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc index eb48373..faf9ba3 100644 --- a/src/platform-openbsd.cc +++ b/src/platform-openbsd.cc @@ -397,12 +397,4 @@ bool VirtualMemory::HasLazyCommits() { return false; } - -void OS::SetUp() { - // Seed the random number generator. We preserve microsecond resolution. - uint64_t seed = static_cast(TimeCurrentMillis()) ^ (getpid() << 16); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-posix.cc b/src/platform-posix.cc index b111213..fe27eaf 100644 --- a/src/platform-posix.cc +++ b/src/platform-posix.cc @@ -69,6 +69,7 @@ #include "v8.h" #include "codegen.h" +#include "isolate-inl.h" #include "platform.h" namespace v8 { @@ -171,17 +172,14 @@ void* OS::GetRandomMmapAddr() { // CpuFeatures::Probe. We don't care about randomization in this case because // the code page is immediately freed. if (isolate != NULL) { + uintptr_t raw_addr; + isolate->random_number_generator()->NextBytes(&raw_addr, sizeof(raw_addr)); #if V8_TARGET_ARCH_X64 - uint64_t rnd1 = V8::RandomPrivate(isolate); - uint64_t rnd2 = V8::RandomPrivate(isolate); - uint64_t raw_addr = (rnd1 << 32) ^ rnd2; // Currently available CPUs have 48 bits of virtual addressing. Truncate // the hint address to 46 bits to give the kernel a fighting chance of // fulfilling our placement request. raw_addr &= V8_UINT64_C(0x3ffffffff000); #else - uint32_t raw_addr = V8::RandomPrivate(isolate); - raw_addr &= 0x3ffff000; # ifdef __sun diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc index d8b60c3..66f23ae 100644 --- a/src/platform-solaris.cc +++ b/src/platform-solaris.cc @@ -361,16 +361,4 @@ bool VirtualMemory::HasLazyCommits() { return false; } - -void OS::SetUp() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly will cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast(TimeCurrentMillis()); - srandom(static_cast(seed)); -} - - } } // namespace v8::internal diff --git a/src/platform-win32.cc b/src/platform-win32.cc index 8247d2e..83bfc89 100644 --- a/src/platform-win32.cc +++ b/src/platform-win32.cc @@ -43,6 +43,7 @@ #include "v8.h" #include "codegen.h" +#include "isolate-inl.h" #include "platform.h" #include "simulator.h" #include "vm-state-inl.h" @@ -124,13 +125,6 @@ int strncpy_s(char* dest, size_t dest_size, const char* source, size_t count) { #endif // __MINGW32__ -// Generate a pseudo-random number in the range 0-2^31-1. Usually -// defined in stdlib.h. Missing in both Microsoft Visual Studio C++ and MinGW. -int random() { - return rand(); -} - - namespace v8 { namespace internal { @@ -794,8 +788,9 @@ void* OS::GetRandomMmapAddr() { static const intptr_t kAllocationRandomAddressMin = 0x04000000; static const intptr_t kAllocationRandomAddressMax = 0x3FFF0000; #endif - uintptr_t address = (V8::RandomPrivate(isolate) << kPageSizeBits) - | kAllocationRandomAddressMin; + uintptr_t address = + (isolate->random_number_generator()->NextInt() << kPageSizeBits) | + kAllocationRandomAddressMin; address &= kAllocationRandomAddressMax; return reinterpret_cast(address); } @@ -1580,15 +1575,4 @@ void Thread::YieldCPU() { Sleep(0); } - -void OS::SetUp() { - // Seed the random number generator. - // Convert the current time to a 64-bit integer first, before converting it - // to an unsigned. Going directly can cause an overflow and the seed to be - // set to all ones. The seed will be identical for different instances that - // call this setup code within the same millisecond. - uint64_t seed = static_cast(TimeCurrentMillis()); - srand(static_cast(seed)); -} - } } // namespace v8::internal diff --git a/src/platform.h b/src/platform.h index 6ae3fa0..428ba55 100644 --- a/src/platform.h +++ b/src/platform.h @@ -86,11 +86,6 @@ inline int lrint(double flt) { #endif // V8_CC_MSVC -// Random is missing on both Visual Studio and MinGW. -#if V8_CC_MSVC || V8_CC_MINGW -int random(); -#endif // V8_CC_MSVC || V8_CC_MINGW - namespace v8 { namespace internal { @@ -171,9 +166,6 @@ inline intptr_t InternalGetExistingThreadLocal(intptr_t index) { class OS { public: - // Initializes the platform OS support. Called once at VM startup. - static void SetUp(); - // Initializes the platform OS support that depend on CPU features. This is // called after CPU initialization. static void PostSetUp(); diff --git a/src/platform/time.h b/src/platform/time.h index 3fed628..2ce6cdd 100644 --- a/src/platform/time.h +++ b/src/platform/time.h @@ -222,6 +222,15 @@ class Time V8_FINAL BASE_EMBEDDED { // with which we might compare it. static Time Max() { return Time(std::numeric_limits::max()); } + // Converts to/from internal values. The meaning of the "internal value" is + // completely up to the implementation, so it should be treated as opaque. + static Time FromInternalValue(int64_t value) { + return Time(value); + } + int64_t ToInternalValue() const { + return us_; + } + // Converts to/from POSIX time specs. static Time FromTimespec(struct timespec ts); struct timespec ToTimespec() const; @@ -329,6 +338,15 @@ class TimeTicks V8_FINAL BASE_EMBEDDED { // Returns true if this object has not been initialized. bool IsNull() const { return ticks_ == 0; } + // Converts to/from internal values. The meaning of the "internal value" is + // completely up to the implementation, so it should be treated as opaque. + static TimeTicks FromInternalValue(int64_t value) { + return TimeTicks(value); + } + int64_t ToInternalValue() const { + return ticks_; + } + TimeTicks& operator=(const TimeTicks other) { ticks_ = other.ticks_; return *this; diff --git a/src/utils/random-number-generator.cc b/src/utils/random-number-generator.cc new file mode 100644 index 0000000..1e03ee2 --- /dev/null +++ b/src/utils/random-number-generator.cc @@ -0,0 +1,136 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "utils/random-number-generator.h" + +#include + +#include "flags.h" +#include "platform/mutex.h" +#include "platform/time.h" +#include "utils.h" + +namespace v8 { +namespace internal { + +static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER; +static RandomNumberGenerator::EntropySource entropy_source = NULL; + + +// static +void RandomNumberGenerator::SetEntropySource(EntropySource source) { + LockGuard lock_guard(entropy_mutex.Pointer()); + entropy_source = source; +} + + +RandomNumberGenerator::RandomNumberGenerator() { + // Check --random-seed flag first. + if (FLAG_random_seed != 0) { + SetSeed(FLAG_random_seed); + return; + } + + // Check if embedder supplied an entropy source. + { LockGuard lock_guard(entropy_mutex.Pointer()); + if (entropy_source != NULL) { + int64_t seed; + if (entropy_source(reinterpret_cast(&seed), + sizeof(seed))) { + SetSeed(seed); + return; + } + } + } + + // Gather entropy from /dev/urandom if available. + FILE* fp = fopen("/dev/urandom", "rb"); + if (fp != NULL) { + int64_t seed; + size_t n = fread(&seed, sizeof(seed), 1, fp); + fclose(fp); + if (n == 1) { + SetSeed(seed); + return; + } + } + + // We cannot assume that random() or rand() were seeded + // properly, so instead of relying on random() or rand(), + // we just seed our PRNG using timing data as fallback. + int64_t seed = Time::NowFromSystemTime().ToInternalValue() << 24; + seed ^= TimeTicks::HighResNow().ToInternalValue() << 16; + seed ^= TimeTicks::Now().ToInternalValue() << 8; + SetSeed(seed); +} + + +int RandomNumberGenerator::NextInt(int max) { + ASSERT_LE(0, max); + + // Fast path if max is a power of 2. + if (IsPowerOf2(max)) { + return static_cast((max * static_cast(Next(31))) >> 31); + } + + while (true) { + int rnd = Next(31); + int val = rnd % max; + if (rnd - val + (max - 1) >= 0) { + return val; + } + } +} + + +double RandomNumberGenerator::NextDouble() { + return ((static_cast(Next(26)) << 27) + Next(27)) / + static_cast(static_cast(1) << 53); +} + + +void RandomNumberGenerator::NextBytes(void* buffer, size_t buflen) { + for (size_t n = 0; n < buflen; ++n) { + static_cast(buffer)[n] = static_cast(Next(8)); + } +} + + +int RandomNumberGenerator::Next(int bits) { + ASSERT_LT(0, bits); + ASSERT_GE(32, bits); + int64_t seed = (seed_ * kMultiplier + kAddend) & kMask; + seed_ = seed; + return static_cast(seed >> (48 - bits)); +} + + +void RandomNumberGenerator::SetSeed(int64_t seed) { + seed_ = (seed ^ kMultiplier) & kMask; +} + +} } // namespace v8::internal diff --git a/src/utils/random-number-generator.h b/src/utils/random-number-generator.h new file mode 100644 index 0000000..fc14ef4 --- /dev/null +++ b/src/utils/random-number-generator.h @@ -0,0 +1,106 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_UTILS_RANDOM_NUMBER_GENERATOR_H_ +#define V8_UTILS_RANDOM_NUMBER_GENERATOR_H_ + +#include "globals.h" + +namespace v8 { +namespace internal { + +// ----------------------------------------------------------------------------- +// RandomNumberGenerator +// +// This class is used to generate a stream of pseudorandom numbers. The class +// uses a 48-bit seed, which is modified using a linear congruential formula. +// (See Donald Knuth, The Art of Computer Programming, Volume 3, Section 3.2.1.) +// If two instances of RandomNumberGenerator are created with the same seed, and +// the same sequence of method calls is made for each, they will generate and +// return identical sequences of numbers. +// This class is neither reentrant nor threadsafe. + +class RandomNumberGenerator V8_FINAL { + public: + // EntropySource is used as a callback function when V8 needs a source of + // entropy. + typedef bool (*EntropySource)(unsigned char* buffer, size_t buflen); + static void SetEntropySource(EntropySource entropy_source); + + RandomNumberGenerator(); + explicit RandomNumberGenerator(int64_t seed) { SetSeed(seed); } + + // Returns the next pseudorandom, uniformly distributed int value from this + // random number generator's sequence. The general contract of |NextInt()| is + // that one int value is pseudorandomly generated and returned. + // All 2^32 possible integer values are produced with (approximately) equal + // probability. + V8_INLINE(int NextInt()) V8_WARN_UNUSED_RESULT { + return Next(32); + } + + // Returns a pseudorandom, uniformly distributed int value between 0 + // (inclusive) and the specified max value (exclusive), drawn from this random + // number generator's sequence. The general contract of |NextInt(int)| is that + // one int value in the specified range is pseudorandomly generated and + // returned. All max possible int values are produced with (approximately) + // equal probability. + int NextInt(int max) V8_WARN_UNUSED_RESULT; + + // Returns the next pseudorandom, uniformly distributed boolean value from + // this random number generator's sequence. The general contract of + // |NextBoolean()| is that one boolean value is pseudorandomly generated and + // returned. The values true and false are produced with (approximately) equal + // probability. + V8_INLINE(bool NextBool()) V8_WARN_UNUSED_RESULT { + return Next(1) != 0; + } + + // Returns the next pseudorandom, uniformly distributed double value between + // 0.0 and 1.0 from this random number generator's sequence. + // The general contract of |NextDouble()| is that one double value, chosen + // (approximately) uniformly from the range 0.0 (inclusive) to 1.0 + // (exclusive), is pseudorandomly generated and returned. + double NextDouble() V8_WARN_UNUSED_RESULT; + + // Fills the elements of a specified array of bytes with random numbers. + void NextBytes(void* buffer, size_t buflen); + + private: + static const int64_t kMultiplier = V8_2PART_UINT64_C(0x5, deece66d); + static const int64_t kAddend = 0xb; + static const int64_t kMask = V8_2PART_UINT64_C(0xffff, ffffffff); + + int Next(int bits) V8_WARN_UNUSED_RESULT; + void SetSeed(int64_t seed); + + int64_t seed_; +}; + +} } // namespace v8::internal + +#endif // V8_UTILS_RANDOM_NUMBER_GENERATOR_H_ diff --git a/src/v8.cc b/src/v8.cc index d9ce840..e894164 100644 --- a/src/v8.cc +++ b/src/v8.cc @@ -53,10 +53,6 @@ V8_DECLARE_ONCE(init_once); List* V8::call_completed_callbacks_ = NULL; v8::ArrayBuffer::Allocator* V8::array_buffer_allocator_ = NULL; -static LazyMutex entropy_mutex = LAZY_MUTEX_INITIALIZER; - -static EntropySource entropy_source; - bool V8::Initialize(Deserializer* des) { InitializeOncePerProcess(); @@ -107,41 +103,6 @@ void V8::TearDown() { } -static void seed_random(uint32_t* state) { - for (int i = 0; i < 2; ++i) { - if (FLAG_random_seed != 0) { - state[i] = FLAG_random_seed; - } else if (entropy_source != NULL) { - uint32_t val; - LockGuard lock_guard(entropy_mutex.Pointer()); - entropy_source(reinterpret_cast(&val), sizeof(uint32_t)); - state[i] = val; - } else { - state[i] = random(); - } - } -} - - -// Random number generator using George Marsaglia's MWC algorithm. -static uint32_t random_base(uint32_t* state) { - // Initialize seed using the system random(). - // No non-zero seed will ever become zero again. - if (state[0] == 0) seed_random(state); - - // Mix the bits. Never replaces state[i] with 0 if it is nonzero. - state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16); - state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16); - - return (state[0] << 14) + (state[1] & 0x3FFFF); -} - - -void V8::SetEntropySource(EntropySource source) { - entropy_source = source; -} - - void V8::SetReturnAddressLocationResolver( ReturnAddressLocationResolver resolver) { StackFrame::SetReturnAddressLocationResolver(resolver); @@ -152,15 +113,18 @@ void V8::SetReturnAddressLocationResolver( uint32_t V8::Random(Context* context) { ASSERT(context->IsNativeContext()); ByteArray* seed = context->random_seed(); - return random_base(reinterpret_cast(seed->GetDataStartAddress())); -} + uint32_t* state = reinterpret_cast(seed->GetDataStartAddress()); + + // When we get here, the RNG must have been initialized, + // see the Genesis constructor in file bootstrapper.cc. + ASSERT_NE(0, state[0]); + ASSERT_NE(0, state[1]); + // Mix the bits. Never replaces state[i] with 0 if it is nonzero. + state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16); + state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16); -// Used internally by the JIT and memory allocator for security -// purposes. So, we keep a different state to prevent informations -// leaks that could be used in an exploit. -uint32_t V8::RandomPrivate(Isolate* isolate) { - return random_base(isolate->private_random_seed()); + return (state[0] << 14) + (state[1] & 0x3FFFF); } @@ -282,7 +246,6 @@ void V8::InitializeOncePerProcessImpl() { FLAG_concurrent_recompilation = false; } - OS::SetUp(); Sampler::SetUp(); CPU::SetUp(); OS::PostSetUp(); diff --git a/src/v8.h b/src/v8.h index 23277b3..5848f74 100644 --- a/src/v8.h +++ b/src/v8.h @@ -97,11 +97,6 @@ class V8 : public AllStatic { static void SetFunctionEntryHook(FunctionEntryHook entry_hook); // Random number generation support. Not cryptographically safe. static uint32_t Random(Context* context); - // We use random numbers internally in memory allocation and in the - // compilers for security. In order to prevent information leaks we - // use a separate random state for internal random number - // generation. - static uint32_t RandomPrivate(Isolate* isolate); static Object* FillHeapNumberWithRandom(Object* heap_number, Context* context); diff --git a/test/cctest/cctest.gyp b/test/cctest/cctest.gyp index 75d26b6..0775cc5 100644 --- a/test/cctest/cctest.gyp +++ b/test/cctest/cctest.gyp @@ -91,6 +91,7 @@ 'test-platform-tls.cc', 'test-profile-generator.cc', 'test-random.cc', + 'test-random-number-generator.cc', 'test-regexp.cc', 'test-reloc-info.cc', 'test-semaphore.cc', diff --git a/test/cctest/test-assembler-x64.cc b/test/cctest/test-assembler-x64.cc index d5aaf4f..f7d2311 100644 --- a/test/cctest/test-assembler-x64.cc +++ b/test/cctest/test-assembler-x64.cc @@ -90,7 +90,6 @@ static const v8::internal::Register arg2 = rsi; TEST(AssemblerX64ReturnOperation) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -113,7 +112,6 @@ TEST(AssemblerX64ReturnOperation) { TEST(AssemblerX64StackOperations) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -146,7 +144,6 @@ TEST(AssemblerX64StackOperations) { TEST(AssemblerX64ArithmeticOperations) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -169,7 +166,6 @@ TEST(AssemblerX64ArithmeticOperations) { TEST(AssemblerX64ImulOperation) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -198,7 +194,6 @@ TEST(AssemblerX64ImulOperation) { TEST(AssemblerX64MemoryOperands) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -233,7 +228,6 @@ TEST(AssemblerX64MemoryOperands) { TEST(AssemblerX64ControlFlow) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, @@ -263,7 +257,6 @@ TEST(AssemblerX64ControlFlow) { TEST(AssemblerX64LoopImmediates) { - OS::SetUp(); // Allocate an executable page of memory. size_t actual_size; byte* buffer = static_cast(OS::Allocate(Assembler::kMinimalBufferSize, diff --git a/test/cctest/test-platform-linux.cc b/test/cctest/test-platform-linux.cc index 34ae43c..f289e94 100644 --- a/test/cctest/test-platform-linux.cc +++ b/test/cctest/test-platform-linux.cc @@ -40,7 +40,6 @@ using namespace ::v8::internal; TEST(VirtualMemory) { - OS::SetUp(); VirtualMemory* vm = new VirtualMemory(1 * MB); CHECK(vm->IsReserved()); void* block_addr = vm->address(); @@ -55,6 +54,5 @@ TEST(VirtualMemory) { TEST(GetCurrentProcessId) { - OS::SetUp(); CHECK_EQ(static_cast(getpid()), OS::GetCurrentProcessId()); } diff --git a/test/cctest/test-platform-win32.cc b/test/cctest/test-platform-win32.cc index a5089d3..d7fdab1 100644 --- a/test/cctest/test-platform-win32.cc +++ b/test/cctest/test-platform-win32.cc @@ -39,7 +39,6 @@ using namespace ::v8::internal; TEST(VirtualMemory) { - OS::SetUp(); VirtualMemory* vm = new VirtualMemory(1 * MB); CHECK(vm->IsReserved()); void* block_addr = vm->address(); @@ -54,7 +53,6 @@ TEST(VirtualMemory) { TEST(GetCurrentProcessId) { - OS::SetUp(); CHECK_EQ(static_cast(::GetCurrentProcessId()), OS::GetCurrentProcessId()); } diff --git a/test/cctest/test-random-number-generator.cc b/test/cctest/test-random-number-generator.cc new file mode 100644 index 0000000..d9fadfa --- /dev/null +++ b/test/cctest/test-random-number-generator.cc @@ -0,0 +1,92 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "cctest.h" +#include "utils/random-number-generator.h" + +using namespace v8::internal; + + +static const int kMaxRuns = 12345; +static const int kRandomSeeds[] = { + -1, 1, 42, 100, 1234567890, 987654321, 0xdeadbeef +}; + + +TEST(NextIntWithMaxValue) { + for (unsigned n = 0; n < ARRAY_SIZE(kRandomSeeds); ++n) { + RandomNumberGenerator rng(kRandomSeeds[n]); + for (int max = 1; max <= kMaxRuns; ++max) { + int n = rng.NextInt(max); + CHECK_LE(0, n); + CHECK_LT(n, max); + } + } +} + + +TEST(NextBoolReturnsBooleanValue) { + for (unsigned n = 0; n < ARRAY_SIZE(kRandomSeeds); ++n) { + RandomNumberGenerator rng(kRandomSeeds[n]); + for (int k = 0; k < kMaxRuns; ++k) { + bool b = rng.NextBool(); + CHECK(b == false || b == true); + } + } +} + + +TEST(NextDoubleRange) { + for (unsigned n = 0; n < ARRAY_SIZE(kRandomSeeds); ++n) { + RandomNumberGenerator rng(kRandomSeeds[n]); + for (int k = 0; k < kMaxRuns; ++k) { + double d = rng.NextDouble(); + CHECK_LE(0.0, d); + CHECK_LT(d, 1.0); + } + } +} + + +TEST(RandomSeedFlagIsUsed) { + for (unsigned n = 0; n < ARRAY_SIZE(kRandomSeeds); ++n) { + FLAG_random_seed = kRandomSeeds[n]; + RandomNumberGenerator rng1; + RandomNumberGenerator rng2(kRandomSeeds[n]); + for (int k = 1; k <= kMaxRuns; ++k) { + int64_t i1, i2; + rng1.NextBytes(&i1, sizeof(i1)); + rng2.NextBytes(&i2, sizeof(i2)); + CHECK_EQ(i2, i1); + CHECK_EQ(rng2.NextInt(), rng1.NextInt()); + CHECK_EQ(rng2.NextInt(k), rng1.NextInt(k)); + CHECK_EQ(rng2.NextDouble(), rng1.NextDouble()); + } + } +} diff --git a/test/cctest/test-random.cc b/test/cctest/test-random.cc index 804546a..4227326 100644 --- a/test/cctest/test-random.cc +++ b/test/cctest/test-random.cc @@ -96,14 +96,5 @@ TEST(CrankshaftRandom) { // Test with some random values. TestSeeds(fun, context, 0xC0C0AFFE, 0x31415926); TestSeeds(fun, context, 0x01020304, 0xFFFFFFFF); - TestSeeds(fun, context, 0x00000001, 0x00000000); - - // Test that we bail out to runtime when seeds are uninitialized (zeros). - SetSeeds(seeds, 0, 0); - Handle value = - Execution::Call(isolate, fun, global, 0, NULL, &has_pending_exception); - CHECK(value->IsHeapNumber()); - CHECK(fun->IsOptimized()); - double crankshaft_value = HeapNumber::cast(*value)->value(); - CHECK_NE(0.0, crankshaft_value); + TestSeeds(fun, context, 0x00000001, 0x00000001); } diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc index 1f362d7..3326a01 100644 --- a/test/cctest/test-spaces.cc +++ b/test/cctest/test-spaces.cc @@ -207,7 +207,6 @@ static unsigned int Pseudorandom() { TEST(MemoryChunk) { - OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); @@ -264,7 +263,6 @@ TEST(MemoryChunk) { TEST(MemoryAllocator) { - OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); @@ -314,7 +312,6 @@ TEST(MemoryAllocator) { TEST(NewSpace) { - OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); @@ -344,7 +341,6 @@ TEST(NewSpace) { TEST(OldSpace) { - OS::SetUp(); Isolate* isolate = Isolate::Current(); isolate->InitializeLoggingAndCounters(); Heap* heap = isolate->heap(); diff --git a/test/cctest/test-strings.cc b/test/cctest/test-strings.cc index 726188d..9049df1 100644 --- a/test/cctest/test-strings.cc +++ b/test/cctest/test-strings.cc @@ -41,9 +41,9 @@ #include "zone-inl.h" // Adapted from http://en.wikipedia.org/wiki/Multiply-with-carry -class RandomNumberGenerator { +class MyRandomNumberGenerator { public: - RandomNumberGenerator() { + MyRandomNumberGenerator() { init(); } @@ -133,7 +133,7 @@ class AsciiResource: public v8::String::ExternalAsciiStringResource, static void InitializeBuildingBlocks(Handle* building_blocks, int bb_length, bool long_blocks, - RandomNumberGenerator* rng, + MyRandomNumberGenerator* rng, Zone* zone) { // A list of pointers that we don't have any interest in cleaning up. // If they are reachable from a root then leak detection won't complain. @@ -276,7 +276,7 @@ class ConsStringGenerationData { // Cached data. Handle building_blocks_[kNumberOfBuildingBlocks]; String* empty_string_; - RandomNumberGenerator rng_; + MyRandomNumberGenerator rng_; // Stats. ConsStringStats stats_; unsigned early_terminations_; diff --git a/test/cctest/test-strtod.cc b/test/cctest/test-strtod.cc index 952f57c..e7d8576 100644 --- a/test/cctest/test-strtod.cc +++ b/test/cctest/test-strtod.cc @@ -432,7 +432,7 @@ static uint32_t DeterministicRandom() { static uint32_t lo = 0; // Initialization values don't have any special meaning. (They are the result - // of two calls to random().) + // of two calls to rand().) if (hi == 0) hi = 0xbfe166e7; if (lo == 0) lo = 0x64d1c3c9; @@ -448,12 +448,13 @@ static const int kShortStrtodRandomCount = 2; static const int kLargeStrtodRandomCount = 2; TEST(RandomStrtod) { + srand(time(NULL)); char buffer[kBufferSize]; for (int length = 1; length < 15; length++) { for (int i = 0; i < kShortStrtodRandomCount; ++i) { int pos = 0; for (int j = 0; j < length; ++j) { - buffer[pos++] = random() % 10 + '0'; + buffer[pos++] = rand() % 10 + '0'; } int exponent = DeterministicRandom() % (25*2 + 1) - 25 - length; buffer[pos] = '\0'; @@ -466,7 +467,7 @@ TEST(RandomStrtod) { for (int i = 0; i < kLargeStrtodRandomCount; ++i) { int pos = 0; for (int j = 0; j < length; ++j) { - buffer[pos++] = random() % 10 + '0'; + buffer[pos++] = rand() % 10 + '0'; } int exponent = DeterministicRandom() % (308*2 + 1) - 308 - length; buffer[pos] = '\0'; diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc index 541c423..feff477 100644 --- a/test/cctest/test-utils.cc +++ b/test/cctest/test-utils.cc @@ -140,7 +140,6 @@ void TestMemMove(byte* area1, TEST(MemMove) { v8::V8::Initialize(); - OS::SetUp(); byte* area1 = new byte[kAreaSize]; byte* area2 = new byte[kAreaSize]; byte* area3 = new byte[kAreaSize]; diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 5a5a2ae..5d4933a 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -531,6 +531,8 @@ '../../src/utils-inl.h', '../../src/utils.cc', '../../src/utils.h', + '../../src/utils/random-number-generator.cc', + '../../src/utils/random-number-generator.h', '../../src/v8-counters.cc', '../../src/v8-counters.h', '../../src/v8.cc',