Make QUIC insecure randomness thread-safe
cl/358886449 accidentally causes undefined behavior by introducing insecure randomness in a thread-unsafe way, even though QuicRandom is expected to be thread-safe. This CL switches the random number generator state to thread_local storage to avoid the issue. This is slightly slower than before, but still 100x faster than secure randomness. This CL replaces the feature flag to ensure the thread-unsafe code does not get enabled.
Protected by FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_random.
PiperOrigin-RevId: 358941810
Change-Id: Ie00942a0fad3f459a9eeff1a32a34af73da7f5d0
diff --git a/quic/core/crypto/quic_random.cc b/quic/core/crypto/quic_random.cc
index 8292e9b..56657b5 100644
--- a/quic/core/crypto/quic_random.cc
+++ b/quic/core/crypto/quic_random.cc
@@ -8,15 +8,43 @@
#include "third_party/boringssl/src/include/openssl/rand.h"
#include "quic/platform/api/quic_bug_tracker.h"
+#include "quic/platform/api/quic_logging.h"
#include "common/platform/api/quiche_logging.h"
namespace quic {
namespace {
+// Insecure randomness in DefaultRandom uses an implementation of
+// xoshiro256++ 1.0 based on code in the public domain from
+// <http://prng.di.unimi.it/xoshiro256plusplus.c>.
+
+inline uint64_t Xoshiro256PlusPlusRotLeft(uint64_t x, int k) {
+ return (x << k) | (x >> (64 - k));
+}
+
+uint64_t Xoshiro256PlusPlus() {
+ static thread_local uint64_t rng_state[4];
+ static thread_local bool rng_state_initialized = false;
+ if (QUIC_PREDICT_FALSE(!rng_state_initialized)) {
+ RAND_bytes(reinterpret_cast<uint8_t*>(&rng_state), sizeof(rng_state));
+ rng_state_initialized = true;
+ }
+ const uint64_t result =
+ Xoshiro256PlusPlusRotLeft(rng_state[0] + rng_state[3], 23) + rng_state[0];
+ const uint64_t t = rng_state[1] << 17;
+ rng_state[2] ^= rng_state[0];
+ rng_state[3] ^= rng_state[1];
+ rng_state[1] ^= rng_state[2];
+ rng_state[0] ^= rng_state[3];
+ rng_state[2] ^= t;
+ rng_state[3] = Xoshiro256PlusPlusRotLeft(rng_state[3], 45);
+ return result;
+}
+
class DefaultRandom : public QuicRandom {
public:
- DefaultRandom();
+ DefaultRandom() {}
DefaultRandom(const DefaultRandom&) = delete;
DefaultRandom& operator=(const DefaultRandom&) = delete;
~DefaultRandom() override {}
@@ -26,13 +54,8 @@
uint64_t RandUint64() override;
void InsecureRandBytes(void* data, size_t len) override;
uint64_t InsecureRandUint64() override;
-
- private:
- QuicInsecureRandom insecure_random_;
};
-DefaultRandom::DefaultRandom() : insecure_random_(this) {}
-
void DefaultRandom::RandBytes(void* data, size_t len) {
RAND_bytes(reinterpret_cast<uint8_t*>(data), len);
}
@@ -44,11 +67,21 @@
}
void DefaultRandom::InsecureRandBytes(void* data, size_t len) {
- insecure_random_.InsecureRandBytes(data, len);
+ while (len >= sizeof(uint64_t)) {
+ uint64_t random_bytes64 = Xoshiro256PlusPlus();
+ memcpy(data, &random_bytes64, sizeof(uint64_t));
+ data = reinterpret_cast<char*>(data) + sizeof(uint64_t);
+ len -= sizeof(uint64_t);
+ }
+ if (len > 0) {
+ QUICHE_DCHECK_LT(len, sizeof(uint64_t));
+ uint64_t random_bytes64 = Xoshiro256PlusPlus();
+ memcpy(data, &random_bytes64, len);
+ }
}
uint64_t DefaultRandom::InsecureRandUint64() {
- return insecure_random_.InsecureRandUint64();
+ return Xoshiro256PlusPlus();
}
} // namespace
@@ -59,44 +92,4 @@
return random;
}
-// QuicInsecureRandom uses an implementation of xoshiro256++ 1.0 based on code
-// in the public domain from <http://prng.di.unimi.it/xoshiro256plusplus.c>.
-
-namespace {
-inline uint64_t Xoshiro256PlusPlusRotl(uint64_t x, int k) {
- return (x << k) | (x >> (64 - k));
-}
-} // namespace
-
-QuicInsecureRandom::QuicInsecureRandom(QuicRandom* random) {
- random->RandBytes(&rng_state_, sizeof(rng_state_));
-}
-
-void QuicInsecureRandom::InsecureRandBytes(void* data, size_t len) {
- while (len >= sizeof(uint64_t)) {
- uint64_t random_bytes64 = InsecureRandUint64();
- memcpy(data, &random_bytes64, sizeof(uint64_t));
- data = reinterpret_cast<char*>(data) + sizeof(uint64_t);
- len -= sizeof(uint64_t);
- }
- if (len > 0) {
- QUICHE_DCHECK_LT(len, sizeof(uint64_t));
- uint64_t random_bytes64 = InsecureRandUint64();
- memcpy(data, &random_bytes64, len);
- }
-}
-
-uint64_t QuicInsecureRandom::InsecureRandUint64() {
- const uint64_t result =
- Xoshiro256PlusPlusRotl(rng_state_[0] + rng_state_[3], 23) + rng_state_[0];
- const uint64_t t = rng_state_[1] << 17;
- rng_state_[2] ^= rng_state_[0];
- rng_state_[3] ^= rng_state_[1];
- rng_state_[1] ^= rng_state_[2];
- rng_state_[0] ^= rng_state_[3];
- rng_state_[2] ^= t;
- rng_state_[3] = Xoshiro256PlusPlusRotl(rng_state_[3], 45);
- return result;
-}
-
} // namespace quic
diff --git a/quic/core/crypto/quic_random.h b/quic/core/crypto/quic_random.h
index ff1c4bb..4d7bf94 100644
--- a/quic/core/crypto/quic_random.h
+++ b/quic/core/crypto/quic_random.h
@@ -36,27 +36,6 @@
virtual uint64_t InsecureRandUint64() = 0;
};
-// A class that generates non-cryptographically-secure random numbers. It uses
-// a QuicRandom instance to seed its initial randomness. This MUST NOT be used
-// for any application that requires cryptographically-secure randomness.
-class QUIC_EXPORT_PRIVATE QuicInsecureRandom {
- public:
- // |random| is only used during construction of the QuicInsecureRandom to seed
- // its inital state.
- explicit QuicInsecureRandom(QuicRandom* random);
-
- // Generates |len| random bytes in the |data| buffer. This MUST NOT be used
- // for any application that requires cryptographically-secure randomness.
- void InsecureRandBytes(void* data, size_t len);
-
- // Returns a random number in the range [0, kuint64max]. This MUST NOT be used
- // for any application that requires cryptographically-secure randomness.
- uint64_t InsecureRandUint64();
-
- private:
- uint64_t rng_state_[4];
-};
-
} // namespace quic
#endif // QUICHE_QUIC_CORE_CRYPTO_QUIC_RANDOM_H_
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 9a88972..97930ff 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -56,7 +56,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true)
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_randomness, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset_faster_random, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_false, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_testonly_default_true, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_tls_use_normalized_sni_for_cert_selectioon, false)
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc
index 432bd13..fdccef9 100644
--- a/quic/core/quic_framer.cc
+++ b/quic/core/quic_framer.cc
@@ -1292,8 +1292,8 @@
// from comparing the entire packet to a known value. Therefore it has no
// cryptographic use, and does not need a secure cryptographic pseudo-random
// number generator. It's therefore safe to use WriteInsecureRandomBytes here.
- if (GetQuicReloadableFlag(quic_stateless_reset_faster_randomness)) {
- QUIC_RELOADABLE_FLAG_COUNT(quic_stateless_reset_faster_randomness);
+ if (GetQuicReloadableFlag(quic_stateless_reset_faster_random)) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_stateless_reset_faster_random);
if (!writer.WriteInsecureRandomBytes(
QuicRandom::GetInstance(), kMinRandomBytesLengthInStatelessReset)) {
return nullptr;