File Coverage

deps/libgit2/src/util/rand.c
Criterion Covered Total %
statement 43 65 66.1
branch 5 12 41.6
condition n/a
subroutine n/a
pod n/a
total 48 77 62.3


line stmt bran cond sub pod time code
1             /* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
2              
3             To the extent possible under law, the author has dedicated all copyright
4             and related and neighboring rights to this software to the public domain
5             worldwide. This software is distributed without any warranty.
6              
7             See . */
8              
9             #include "git2_util.h"
10             #include "rand.h"
11             #include "runtime.h"
12              
13             #if defined(GIT_RAND_GETENTROPY)
14             # include
15             #endif
16              
17             static uint64_t state[4];
18             static git_mutex state_lock;
19              
20             typedef union {
21             double f;
22             uint64_t d;
23             } bits;
24              
25             #if defined(GIT_WIN32)
26             GIT_INLINE(int) getseed(uint64_t *seed)
27             {
28             HCRYPTPROV provider;
29             SYSTEMTIME systemtime;
30             FILETIME filetime, idletime, kerneltime, usertime;
31             bits convert;
32              
33             if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL,
34             CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
35             BOOL success = CryptGenRandom(provider, sizeof(uint64_t), (void *)seed);
36             CryptReleaseContext(provider, 0);
37              
38             if (success)
39             return 0;
40             }
41              
42             GetSystemTime(&systemtime);
43             if (!SystemTimeToFileTime(&systemtime, &filetime)) {
44             git_error_set(GIT_ERROR_OS, "could not get time for random seed");
45             return -1;
46             }
47              
48             /* Fall-through: generate a seed from the time and system state */
49             *seed = 0;
50             *seed |= ((uint64_t)filetime.dwLowDateTime << 32);
51             *seed |= ((uint64_t)filetime.dwHighDateTime);
52              
53             GetSystemTimes(&idletime, &kerneltime, &usertime);
54              
55             *seed ^= ((uint64_t)idletime.dwLowDateTime << 32);
56             *seed ^= ((uint64_t)kerneltime.dwLowDateTime);
57             *seed ^= ((uint64_t)usertime.dwLowDateTime << 32);
58              
59             *seed ^= ((uint64_t)idletime.dwHighDateTime);
60             *seed ^= ((uint64_t)kerneltime.dwHighDateTime << 12);
61             *seed ^= ((uint64_t)usertime.dwHighDateTime << 24);
62              
63             *seed ^= ((uint64_t)GetCurrentProcessId() << 32);
64             *seed ^= ((uint64_t)GetCurrentThreadId() << 48);
65              
66             convert.f = git__timer(); *seed ^= (convert.d);
67              
68             /* Mix in the addresses of some functions and variables */
69             *seed ^= (((uint64_t)((uintptr_t)seed) << 32));
70             *seed ^= (((uint64_t)((uintptr_t)&errno)));
71              
72             return 0;
73             }
74              
75             #else
76              
77 87           GIT_INLINE(int) getseed(uint64_t *seed)
78             {
79             struct timeval tv;
80             double loadavg[3];
81             bits convert;
82             int fd;
83              
84             # if defined(GIT_RAND_GETENTROPY)
85             GIT_UNUSED((fd = 0));
86              
87             if (getentropy(seed, sizeof(uint64_t)) == 0)
88             return 0;
89             # else
90             /*
91             * Try to read from /dev/urandom; most modern systems will have
92             * this, but we may be chrooted, etc, so it's not a fatal error
93             */
94 87 50         if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
95 87           ssize_t ret = read(fd, seed, sizeof(uint64_t));
96 87           close(fd);
97              
98 87 50         if (ret == (ssize_t)sizeof(uint64_t))
99 87           return 0;
100             }
101             # endif
102              
103             /* Fall-through: generate a seed from the time and system state */
104 0 0         if (gettimeofday(&tv, NULL) < 0) {
105 0           git_error_set(GIT_ERROR_OS, "could get time for random seed");
106 0           return -1;
107             }
108              
109 0           *seed = 0;
110 0           *seed |= ((uint64_t)tv.tv_usec << 40);
111 0           *seed |= ((uint64_t)tv.tv_sec);
112              
113 0           *seed ^= ((uint64_t)getpid() << 48);
114 0           *seed ^= ((uint64_t)getppid() << 32);
115 0           *seed ^= ((uint64_t)getpgid(0) << 28);
116 0           *seed ^= ((uint64_t)getsid(0) << 16);
117 0           *seed ^= ((uint64_t)getuid() << 8);
118 0           *seed ^= ((uint64_t)getgid());
119              
120             # if defined(GIT_RAND_GETLOADAVG)
121             getloadavg(loadavg, 3);
122              
123             convert.f = loadavg[0]; *seed ^= (convert.d >> 36);
124             convert.f = loadavg[1]; *seed ^= (convert.d);
125             convert.f = loadavg[2]; *seed ^= (convert.d >> 16);
126             # else
127 0           GIT_UNUSED(loadavg[0]);
128             # endif
129              
130 0           convert.f = git__timer(); *seed ^= (convert.d);
131              
132             /* Mix in the addresses of some variables */
133 0           *seed ^= ((uint64_t)((size_t)((void *)seed)) << 32);
134 0           *seed ^= ((uint64_t)((size_t)((void *)&errno)));
135              
136 87           return 0;
137             }
138             #endif
139              
140 0           static void git_rand_global_shutdown(void)
141             {
142 0           git_mutex_free(&state_lock);
143 0           }
144              
145 87           int git_rand_global_init(void)
146             {
147 87           uint64_t seed = 0;
148              
149 87 50         if (git_mutex_init(&state_lock) < 0 || getseed(&seed) < 0)
    50          
150 0           return -1;
151              
152 87 50         if (!seed) {
153 0           git_error_set(GIT_ERROR_INTERNAL, "failed to generate random seed");
154 0           return -1;
155             }
156              
157 87           git_rand_seed(seed);
158 87           git_runtime_shutdown_register(git_rand_global_shutdown);
159              
160 87           return 0;
161             }
162              
163             /*
164             * This is splitmix64. xoroshiro256** uses 256 bit seed; this is used
165             * to generate 256 bits of seed from the given 64, per the author's
166             * recommendation.
167             */
168 348           GIT_INLINE(uint64_t) splitmix64(uint64_t *in)
169             {
170             uint64_t z;
171              
172 348           *in += 0x9e3779b97f4a7c15;
173              
174 348           z = *in;
175 348           z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
176 348           z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
177 348           return z ^ (z >> 31);
178             }
179              
180 87           void git_rand_seed(uint64_t seed)
181             {
182             uint64_t mixer;
183              
184 87           mixer = seed;
185              
186 87           git_mutex_lock(&state_lock);
187 87           state[0] = splitmix64(&mixer);
188 87           state[1] = splitmix64(&mixer);
189 87           state[2] = splitmix64(&mixer);
190 87           state[3] = splitmix64(&mixer);
191 87           git_mutex_unlock(&state_lock);
192 87           }
193              
194             /* This is xoshiro256** 1.0, one of our all-purpose, rock-solid
195             generators. It has excellent (sub-ns) speed, a state (256 bits) that is
196             large enough for any parallel application, and it passes all tests we
197             are aware of.
198              
199             For generating just floating-point numbers, xoshiro256+ is even faster.
200              
201             The state must be seeded so that it is not everywhere zero. If you have
202             a 64-bit seed, we suggest to seed a splitmix64 generator and use its
203             output to fill s. */
204              
205 374           GIT_INLINE(uint64_t) rotl(const uint64_t x, int k) {
206 374           return (x << k) | (x >> (64 - k));
207             }
208              
209 187           uint64_t git_rand_next(void) {
210             uint64_t t, result;
211              
212 187           git_mutex_lock(&state_lock);
213              
214 187           result = rotl(state[1] * 5, 7) * 9;
215              
216 187           t = state[1] << 17;
217              
218 187           state[2] ^= state[0];
219 187           state[3] ^= state[1];
220 187           state[1] ^= state[2];
221 187           state[0] ^= state[3];
222              
223 187           state[2] ^= t;
224              
225 187           state[3] = rotl(state[3], 45);
226              
227 187           git_mutex_unlock(&state_lock);
228              
229 187           return result;
230             }