From 2b22a7823a26bac8c30fd46cc6461e789f6e53e6 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Wed, 15 May 2024 14:14:44 -0700 Subject: [PATCH] Add gRandom and gArbitrary and initFuzz --- Internal.h | 143 +++++++++++++++++++++++++++++++++++++++++------ VersionedMap.cpp | 120 +-------------------------------------- 2 files changed, 127 insertions(+), 136 deletions(-) diff --git a/Internal.h b/Internal.h index 5fcc140..6e1749e 100644 --- a/Internal.h +++ b/Internal.h @@ -265,6 +265,123 @@ bool operator!=(const ArenaAlloc &lhs, const ArenaAlloc &rhs) { // ==================== END ARENA IMPL ==================== +// ==================== BEGIN RANDOM IMPL ==================== + +struct Random { + // *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org + // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) + // + // Modified - mostly c -> c++ + Random() = default; + + Random(uint64_t initState, uint64_t initSeq) { + pcg32_srandom_r(initState, initSeq); + next(); + } + + /// Draws from a uniform distribution of uint32_t's + uint32_t next() { + auto result = next_; + next_ = pcg32_random_r(); + return result; + } + + /// Draws from a uniform distribution of [0, s). From + /// https://arxiv.org/pdf/1805.10941.pdf + uint32_t bounded(uint32_t s) { + assert(s != 0); + uint32_t x = next(); + auto m = uint64_t(x) * uint64_t(s); + auto l = uint32_t(m); + if (l < s) { + uint32_t t = -s % s; + while (l < t) { + x = next(); + m = uint64_t(x) * uint64_t(s); + l = uint32_t(m); + } + } + uint32_t result = m >> 32; + return result; + } + + /// Fill `bytes` with `size` random hex bytes + void randomHex(uint8_t *bytes, int size); + +private: + uint32_t pcg32_random_r() { + uint64_t oldState = state; + // Advance internal state + state = oldState * 6364136223846793005ULL + inc; + // Calculate output function (XSH RR), uses old state for max ILP + uint32_t xorShifted = ((oldState >> 18u) ^ oldState) >> 27u; + uint32_t rot = oldState >> 59u; + return (xorShifted >> rot) | (xorShifted << ((-rot) & 31)); + } + + // Seed the rng. Specified in two parts, state initializer and a + // sequence selection constant (a.k.a. stream id) + void pcg32_srandom_r(uint64_t initstate, uint64_t initSeq) { + state = 0U; + inc = (initSeq << 1u) | 1u; + pcg32_random_r(); + state += initstate; + pcg32_random_r(); + } + uint32_t next_{}; + // RNG state. All values are possible. + uint64_t state{}; + // Controls which RNG sequence (stream) is selected. Must *always* be odd. + uint64_t inc{}; +}; + +inline void Random::randomHex(uint8_t *bytes, int size) { + int i = 0; + while (i + 8 < size) { + uint32_t r = next(); + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + } + uint32_t r = next(); + while (i < size) { + bytes[i++] = "0123456789abcdef"[r & 0b1111]; + r >>= 4; + } +} + +inline Random seededRandom() { + FILE *f = fopen("/dev/urandom", "r"); + if (f == nullptr) { + fprintf(stderr, "Failed to open /dev/urandom\n"); + abort(); + } + uint64_t seed[2]; + if (fread(seed, sizeof(seed[0]), sizeof(seed) / sizeof(seed[0]), f) != + sizeof(seed) / sizeof(seed[0])) { + fprintf(stderr, "Failed to read from /dev/urandom\n"); + abort(); + } + fclose(f); + return Random{seed[0], seed[1]}; +} + +inline thread_local Random gRandom = seededRandom(); + +// ==================== END RANDOM IMPL ==================== + // ==================== BEGIN ARBITRARY IMPL ==================== /// Think of `Arbitrary` as an attacker-controlled random number generator. @@ -290,16 +407,6 @@ struct Arbitrary { /// Draws an arbitrary element from [0, s) uint32_t bounded(uint32_t s); - /// Fill `bytes` with `size` arbitrary bytes - void randomBytes(uint8_t *bytes, int size) { - int toFill = std::min(size, bytecode.size()); - if (toFill > 0) { - memcpy(bytes, bytecode.data(), toFill); - } - bytecode = bytecode.subspan(toFill, bytecode.size() - toFill); - memset(bytes + toFill, 0, size - toFill); - } - /// Fill `bytes` with `size` random hex bytes void randomHex(uint8_t *bytes, int size) { for (int i = 0; i < size;) { @@ -312,13 +419,6 @@ struct Arbitrary { } } - template >> - T randT() { - T t; - randomBytes((uint8_t *)&t, sizeof(T)); - return t; - } - bool hasEntropy() const { return bytecode.size() != 0; } private: @@ -381,6 +481,15 @@ inline uint32_t Arbitrary::bounded(uint32_t s) { } } +inline Arbitrary gArbitrary; + +inline void initFuzz(const uint8_t *data, size_t size) { + gArbitrary = Arbitrary{{data, size}}; + uint64_t state = gArbitrary.next(); + uint64_t seq = gArbitrary.next(); + gRandom = Random{state, seq}; +} + // ==================== END ARBITRARY IMPL ==================== // GCOVR_EXCL_STOP diff --git a/VersionedMap.cpp b/VersionedMap.cpp index 929de54..df3aae1 100644 --- a/VersionedMap.cpp +++ b/VersionedMap.cpp @@ -63,117 +63,6 @@ void munmapSafe(void *ptr, size_t size) { } } -struct Random { - // *Really* minimal PCG32 code / (c) 2014 M.E. O'Neill / pcg-random.org - // Licensed under Apache License 2.0 (NO WARRANTY, etc. see website) - // - // Modified - mostly c -> c++ - Random() = default; - - Random(uint64_t initState, uint64_t initSeq) { - pcg32_srandom_r(initState, initSeq); - next(); - } - - /// Draws from a uniform distribution of uint32_t's - uint32_t next() { - auto result = next_; - next_ = pcg32_random_r(); - return result; - } - - /// Draws from a uniform distribution of [0, s). From - /// https://arxiv.org/pdf/1805.10941.pdf - uint32_t bounded(uint32_t s) { - assert(s != 0); - uint32_t x = next(); - auto m = uint64_t(x) * uint64_t(s); - auto l = uint32_t(m); - if (l < s) { - uint32_t t = -s % s; - while (l < t) { - x = next(); - m = uint64_t(x) * uint64_t(s); - l = uint32_t(m); - } - } - uint32_t result = m >> 32; - return result; - } - - /// Fill `bytes` with `size` random hex bytes - void randomHex(uint8_t *bytes, int size); - -private: - uint32_t pcg32_random_r() { - uint64_t oldState = state; - // Advance internal state - state = oldState * 6364136223846793005ULL + inc; - // Calculate output function (XSH RR), uses old state for max ILP - uint32_t xorShifted = ((oldState >> 18u) ^ oldState) >> 27u; - uint32_t rot = oldState >> 59u; - return (xorShifted >> rot) | (xorShifted << ((-rot) & 31)); - } - - // Seed the rng. Specified in two parts, state initializer and a - // sequence selection constant (a.k.a. stream id) - void pcg32_srandom_r(uint64_t initstate, uint64_t initSeq) { - state = 0U; - inc = (initSeq << 1u) | 1u; - pcg32_random_r(); - state += initstate; - pcg32_random_r(); - } - uint32_t next_{}; - // RNG state. All values are possible. - uint64_t state{}; - // Controls which RNG sequence (stream) is selected. Must *always* be odd. - uint64_t inc{}; -}; - -void Random::randomHex(uint8_t *bytes, int size) { - int i = 0; - while (i + 8 < size) { - uint32_t r = next(); - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - } - uint32_t r = next(); - while (i < size) { - bytes[i++] = "0123456789abcdef"[r & 0b1111]; - r >>= 4; - } -} - -Random seededRandom() { - FILE *f = fopen("/dev/urandom", "r"); - if (f == nullptr) { - fprintf(stderr, "Failed to open /dev/urandom\n"); - abort(); - } - uint64_t seed[2]; - if (fread(seed, sizeof(seed[0]), sizeof(seed) / sizeof(seed[0]), f) != - sizeof(seed) / sizeof(seed[0])) { - fprintf(stderr, "Failed to read from /dev/urandom\n"); - abort(); - } - fclose(f); - return Random{seed[0], seed[1]}; -} - namespace weaselab { // 96 is enough for an entire search path in a tree with a size that @@ -773,7 +662,7 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { // Prepare new node uint32_t node = newNode( pointVersion, rangeVersion, key.p, key.len, val->p, val->len, - inserted ? random.next() : mm.base[finger.backNode()].entry->priority); + inserted ? gRandom.next() : mm.base[finger.backNode()].entry->priority); if (!inserted) { auto &n = mm.base[node]; n.pointer[0] = child(finger.backNode(), false, @@ -945,13 +834,6 @@ struct __attribute__((__visibility__("hidden"))) VersionedMap::Impl { void firstGeq(const Key *key, const int64_t *version, Iterator *iterator, int count) const; - Random random = -#ifndef NDEBUG - {}; -#else - seededRandom(); -#endif - MemManager mm; RootSet roots; // Only meaningful within the callstack of `addMutations`