xref: /llvm-project/llvm/lib/Support/RandomNumberGenerator.cpp (revision ba13fa2a5d57581bff1a7e9322234af30f4882f6)
1 //===-- RandomNumberGenerator.cpp - Implement RNG class -------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements deterministic random number generation (RNG).
10 // The current implementation is NOT cryptographically secure as it uses
11 // the C++11 <random> facilities.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm/Support/RandomNumberGenerator.h"
16 
17 #include "DebugOptions.h"
18 
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/Debug.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Support/raw_ostream.h"
23 #ifdef _WIN32
24 #include "llvm/Support/Windows/WindowsSupport.h"
25 #else
26 #include "Unix/Unix.h"
27 #endif
28 
29 using namespace llvm;
30 
31 #define DEBUG_TYPE "rng"
32 namespace {
33 struct CreateSeed {
34   static void *call() {
35     return new cl::opt<uint64_t>(
36         "rng-seed", cl::value_desc("seed"), cl::Hidden,
37         cl::desc("Seed for the random number generator"), cl::init(0));
38   }
39 };
40 } // namespace
41 static ManagedStatic<cl::opt<uint64_t>, CreateSeed> Seed;
42 void llvm::initRandomSeedOptions() { *Seed; }
43 
44 RandomNumberGenerator::RandomNumberGenerator(StringRef Salt) {
45   LLVM_DEBUG(if (*Seed == 0) dbgs()
46              << "Warning! Using unseeded random number generator.\n");
47 
48   // Combine seed and salts using std::seed_seq.
49   // Data: Seed-low, Seed-high, Salt
50   // Note: std::seed_seq can only store 32-bit values, even though we
51   // are using a 64-bit RNG. This isn't a problem since the Mersenne
52   // twister constructor copies these correctly into its initial state.
53   std::vector<uint32_t> Data;
54   Data.resize(2 + Salt.size());
55   Data[0] = *Seed;
56   Data[1] = *Seed >> 32;
57 
58   llvm::copy(Salt, Data.begin() + 2);
59 
60   std::seed_seq SeedSeq(Data.begin(), Data.end());
61   Generator.seed(SeedSeq);
62 }
63 
64 RandomNumberGenerator::result_type RandomNumberGenerator::operator()() {
65   return Generator();
66 }
67 
68 // Get random vector of specified size
69 std::error_code llvm::getRandomBytes(void *Buffer, size_t Size) {
70 #ifdef _WIN32
71   HCRYPTPROV hProvider;
72   if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL,
73                            CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
74     ScopedCryptContext ScopedHandle(hProvider);
75     if (CryptGenRandom(hProvider, Size, static_cast<BYTE *>(Buffer)))
76       return std::error_code();
77   }
78   return std::error_code(GetLastError(), std::system_category());
79 #else
80   int Fd = open("/dev/urandom", O_RDONLY);
81   if (Fd != -1) {
82     std::error_code Ret;
83     ssize_t BytesRead = read(Fd, Buffer, Size);
84     if (BytesRead == -1)
85       Ret = errnoAsErrorCode();
86     else if (BytesRead != static_cast<ssize_t>(Size))
87       Ret = std::error_code(EIO, std::system_category());
88     if (close(Fd) == -1)
89       Ret = errnoAsErrorCode();
90 
91     return Ret;
92   }
93   return errnoAsErrorCode();
94 #endif
95 }
96