Coverage Report

Created: 2025-07-18 06:12

/src/libgit2/src/util/rand.c
Line
Count
Source (jump to first uncovered line)
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 <http://creativecommons.org/publicdomain/zero/1.0/>. */
8
9
#include "git2_util.h"
10
#include "rand.h"
11
#include "runtime.h"
12
13
#if defined(GIT_WIN32)
14
# include <wincrypt.h>
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
32
  if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL,
33
                          CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
34
    BOOL success = CryptGenRandom(provider, sizeof(uint64_t), (void *)seed);
35
    CryptReleaseContext(provider, 0);
36
37
    if (success)
38
      return 0;
39
  }
40
41
  GetSystemTime(&systemtime);
42
  if (!SystemTimeToFileTime(&systemtime, &filetime)) {
43
    git_error_set(GIT_ERROR_OS, "could not get time for random seed");
44
    return -1;
45
  }
46
47
  /* Fall-through: generate a seed from the time and system state */
48
  *seed = 0;
49
  *seed |= ((uint64_t)filetime.dwLowDateTime << 32);
50
  *seed |= ((uint64_t)filetime.dwHighDateTime);
51
52
  GetSystemTimes(&idletime, &kerneltime, &usertime);
53
54
  *seed ^= ((uint64_t)idletime.dwLowDateTime << 32);
55
  *seed ^= ((uint64_t)kerneltime.dwLowDateTime);
56
  *seed ^= ((uint64_t)usertime.dwLowDateTime << 32);
57
58
  *seed ^= ((uint64_t)idletime.dwHighDateTime);
59
  *seed ^= ((uint64_t)kerneltime.dwHighDateTime << 12);
60
  *seed ^= ((uint64_t)usertime.dwHighDateTime << 24);
61
62
  *seed ^= ((uint64_t)GetCurrentProcessId() << 32);
63
  *seed ^= ((uint64_t)GetCurrentThreadId() << 48);
64
65
  *seed ^= git_time_monotonic();
66
67
  /* Mix in the addresses of some functions and variables */
68
  *seed ^= (((uint64_t)((uintptr_t)seed) << 32));
69
  *seed ^= (((uint64_t)((uintptr_t)&errno)));
70
71
  return 0;
72
}
73
74
#else
75
76
GIT_INLINE(int) getseed(uint64_t *seed)
77
2
{
78
2
  struct timeval tv;
79
2
  int fd;
80
81
2
# if defined(GIT_RAND_GETLOADAVG)
82
2
  double loadavg[3];
83
2
  bits convert;
84
2
# endif
85
86
2
# if defined(GIT_RAND_GETENTROPY)
87
2
  GIT_UNUSED((fd = 0));
88
89
2
  if (getentropy(seed, sizeof(uint64_t)) == 0)
90
2
    return 0;
91
# else
92
  /*
93
   * Try to read from /dev/urandom; most modern systems will have
94
   * this, but we may be chrooted, etc, so it's not a fatal error
95
   */
96
  if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
97
    ssize_t ret = read(fd, seed, sizeof(uint64_t));
98
    close(fd);
99
100
    if (ret == (ssize_t)sizeof(uint64_t))
101
      return 0;
102
  }
103
# endif
104
105
  /* Fall-through: generate a seed from the time and system state */
106
0
  if (gettimeofday(&tv, NULL) < 0) {
107
0
    git_error_set(GIT_ERROR_OS, "could get time for random seed");
108
0
    return -1;
109
0
  }
110
111
0
  *seed = 0;
112
0
  *seed |= ((uint64_t)tv.tv_usec << 40);
113
0
  *seed |= ((uint64_t)tv.tv_sec);
114
115
0
  *seed ^= ((uint64_t)getpid() << 48);
116
0
  *seed ^= ((uint64_t)getppid() << 32);
117
0
  *seed ^= ((uint64_t)getpgid(0) << 28);
118
0
  *seed ^= ((uint64_t)getsid(0) << 16);
119
0
  *seed ^= ((uint64_t)getuid() << 8);
120
0
  *seed ^= ((uint64_t)getgid());
121
122
0
# if defined(GIT_RAND_GETLOADAVG)
123
0
  getloadavg(loadavg, 3);
124
125
0
  convert.f = loadavg[0]; *seed ^= (convert.d >> 36);
126
0
  convert.f = loadavg[1]; *seed ^= (convert.d);
127
0
  convert.f = loadavg[2]; *seed ^= (convert.d >> 16);
128
0
# endif
129
130
0
  *seed ^= git_time_monotonic();
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
0
  return 0;
137
0
}
138
#endif
139
140
static void git_rand_global_shutdown(void)
141
0
{
142
0
  git_mutex_free(&state_lock);
143
0
}
144
145
int git_rand_global_init(void)
146
2
{
147
2
  uint64_t seed = 0;
148
149
2
  if (git_mutex_init(&state_lock) < 0 || getseed(&seed) < 0)
150
0
    return -1;
151
152
2
  if (!seed) {
153
0
    git_error_set(GIT_ERROR_INTERNAL, "failed to generate random seed");
154
0
    return -1;
155
0
  }
156
157
2
  git_rand_seed(seed);
158
2
  git_runtime_shutdown_register(git_rand_global_shutdown);
159
160
2
  return 0;
161
2
}
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
GIT_INLINE(uint64_t) splitmix64(uint64_t *in)
169
8
{
170
8
  uint64_t z;
171
172
8
  *in += 0x9e3779b97f4a7c15;
173
174
8
  z = *in;
175
8
  z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
176
8
  z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
177
8
  return z ^ (z >> 31);
178
8
}
179
180
void git_rand_seed(uint64_t seed)
181
2
{
182
2
  uint64_t mixer;
183
184
2
  mixer = seed;
185
186
2
  git_mutex_lock(&state_lock);
187
2
  state[0] = splitmix64(&mixer);
188
2
  state[1] = splitmix64(&mixer);
189
2
  state[2] = splitmix64(&mixer);
190
2
  state[3] = splitmix64(&mixer);
191
2
  git_mutex_unlock(&state_lock);
192
2
}
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
4
GIT_INLINE(uint64_t) rotl(const uint64_t x, int k) {
206
4
  return (x << k) | (x >> (64 - k));
207
4
}
208
209
2
uint64_t git_rand_next(void) {
210
2
  uint64_t t, result;
211
212
2
  git_mutex_lock(&state_lock);
213
214
2
  result = rotl(state[1] * 5, 7) * 9;
215
216
2
  t = state[1] << 17;
217
218
2
  state[2] ^= state[0];
219
2
  state[3] ^= state[1];
220
2
  state[1] ^= state[2];
221
2
  state[0] ^= state[3];
222
223
2
  state[2] ^= t;
224
225
2
  state[3] = rotl(state[3], 45);
226
227
2
  git_mutex_unlock(&state_lock);
228
229
2
  return result;
230
2
}