1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_uuid.cpp
28 // FIXME: Obviously Unix/Linux specific stuff
29 #include "vogl_uuid.h"
30 #include "vogl_cfile_stream.h"
31 #include "vogl_threading.h"
32 #include "vogl_rand.h"
35 #include <sys/syscall.h>
36 #include <sys/types.h>
43 // init_uuid() is slow (~40ms, maybe slower), and forces a disk flush on a file, so don't call it more than once.
44 // I'm a paranoid nut so this hashes a bunch of shit. It's probably completely overkill for my needs - I should stop reading RFC's.
45 static md5_hash init_uuid()
47 static uint64_t s_counter;
49 // Get as much entropy as we can here
53 memset(p, 0, sizeof(p));
56 timer_ticks tick_hist[N];
57 for (uint i = 0; i < N; i++)
59 uint64_t start_rdtsc = utils::RDTSC();
60 gen.update(start_rdtsc);
62 gen.update(s_counter);
63 gen.update((uint64_t) & s_counter);
66 // Hash stack address of gen_uuid
67 gen.update((uint64_t) & gen_uuid);
69 // Hash the initial timer ticks, and time(NULL)
70 gen.update(timer::get_init_ticks());
71 gen.update((uint64_t)time(NULL));
73 // Hash user ID, name, shell, home dir
74 uid_t uid = geteuid();
76 struct passwd *pw = getpwuid(uid);
77 gen.update((uint64_t) & pw);
80 gen.update(pw, sizeof(struct passwd));
82 gen.update(pw->pw_name, vogl_strlen(pw->pw_name));
84 gen.update(pw->pw_passwd, vogl_strlen(pw->pw_passwd));
86 gen.update(pw->pw_shell, vogl_strlen(pw->pw_shell));
88 gen.update(pw->pw_dir, vogl_strlen(pw->pw_dir));
90 gen.update(pw->pw_gecos, vogl_strlen(pw->pw_gecos));
95 timer_ticks ticks = timer::get_ticks();
98 // This is obviously expensive (and questionable?), only do it once. But it helps us get some entropy from the disk subsystem.
99 // This is also by far the slowest component of this function (~35ms out of ~40ms).
102 uint64_t st = utils::RDTSC();
106 const char *pFilename = "!_!_!_!_!_!_!_vogl_temp!_!_!_!_!_!_!_!_.txt";
107 FILE *pFile = vogl_fopen(pFilename, "wb");
108 gen.update_obj_bits(pFile);
111 fwrite("X", 1, 1, pFile);
113 fsync(fileno(pFile));
118 uint64_t t = utils::RDTSC() - st;
123 gen.update(tm.get_elapsed_ticks());
126 // Grab some bits from /dev/urandom (not /dev/random - it may block for a long time)
130 FILE *fp = vogl_fopen("/dev/urandom", "rb");
131 gen.update_obj_bits(fp);
134 size_t n = fread(buf, 1, N, fp);
138 gen.update(buf, sizeof(buf));
142 // It's fine if some/most/all of these files don't exist, the true/false results get fed into the hash too.
143 // TODO: Double check that all the files we should be able to read are actually getting read and hashed here.
144 #define HASH_FILE(filename) \
147 bool success = cfile_stream::read_file_into_array(filename, buf); \
148 gen.update_obj_bits(success); \
151 HASH_FILE("/proc/sys/kernel/random/entropy_avail");
152 HASH_FILE("/proc/self/statm");
153 HASH_FILE("/proc/self/mounts");
154 HASH_FILE("/proc/self/io");
155 HASH_FILE("/proc/self/smaps");
156 HASH_FILE("/proc/self/stack");
157 HASH_FILE("/proc/self/status");
158 HASH_FILE("/proc/self/maps");
159 HASH_FILE("/proc/self/stat");
160 HASH_FILE("/proc/self/stat");
161 HASH_FILE("/proc/cpuinfo");
162 HASH_FILE("/proc/meminfo");
163 HASH_FILE("/proc/stat");
164 HASH_FILE("/proc/misc");
165 HASH_FILE("/proc/swaps");
166 HASH_FILE("/proc/version");
167 HASH_FILE("/proc/loadavg");
168 HASH_FILE("/proc/interrupts");
169 HASH_FILE("/proc/ioports");
170 HASH_FILE("/proc/partitions");
171 HASH_FILE("/proc/driver/rtc");
172 HASH_FILE("/proc/self/net/wireless");
173 HASH_FILE("/proc/self/net/netstat");
174 HASH_FILE("/proc/self/net/netlink");
175 HASH_FILE("/sys/class/net/eth0/address");
176 HASH_FILE("/sys/class/net/eth1/address");
177 HASH_FILE("/sys/class/net/wlan0/address");
180 gen.update(utils::RDTSC());
182 // Hash thread, process ID's, etc.
183 pid_t tid = (pid_t)syscall(SYS_gettid);
184 gen.update_obj_bits(tid);
186 pid_t pid = getpid();
187 gen.update_obj_bits(pid);
190 gen.update_obj_bits(pid);
191 gen.update((uint64_t) & pid);
193 ticks -= timer::get_ticks();
194 tick_hist[i] = ticks;
197 ticks = timer::get_ticks();
199 // Get some entropy from the stack.
200 char purposely_uninitialized_buf[256];
201 gen.update(purposely_uninitialized_buf, sizeof(purposely_uninitialized_buf));
203 // Get some entropy from the heap.
204 p[i] = vogl_malloc(65536 * (i + 1));
205 gen.update_obj_bits(p[i]);
208 for (uint j = 0; j < 16; j++)
209 gen.update_obj_bits(reinterpret_cast<const uint64_t *>(p)[j]);
213 gettimeofday(&tv, NULL);
214 gen.update_obj_bits(tv);
216 // Hash the current environment
220 gen.update(environ[e], vogl_strlen(environ[e]));
224 uint64_t s = utils::RDTSC();
226 // Try to get some entropy from the scheduler.
229 gen.update(utils::RDTSC() - s);
231 ticks -= timer::get_ticks();
234 gen.update(utils::RDTSC() - start_rdtsc);
237 for (uint i = 1; i < N; i++)
239 uint64_t t = tick_hist[i] - tick_hist[i - 1];
243 for (uint i = 0; i < N; i++)
246 return gen.finalize();
249 static bool g_uuid_initialized;
250 static md5_hash g_uuid_key;
251 static random g_uuid_rand;
252 static mutex g_uuid_mutex;
253 static md5_hash g_prev_uuid;
257 uint64_t s = utils::RDTSC();
259 scoped_mutex scoped_lock(g_uuid_mutex);
261 if (!g_uuid_initialized)
263 g_uuid_key = init_uuid();
265 g_uuid_rand.seed(g_uuid_key);
267 g_uuid_initialized = true;
270 uint32 h[4] = { g_uuid_rand.urand32(), g_uuid_rand.urand32(), g_uuid_rand.urand32(), g_uuid_rand.urand32() };
272 // Throw in some more quick sources of entropy, why not.
273 md5_hash_gen gen(h, sizeof(h));
274 gen.update_obj_bits(g_uuid_key);
275 gen.update_obj_bits(g_prev_uuid);
278 gen.update((uint64_t) & s);
280 uint64_t purposely_uninitialized_variable;
281 gen.update(&purposely_uninitialized_variable, sizeof(purposely_uninitialized_variable));
283 static uint64_t s_counter;
285 gen.update(s_counter);
288 gettimeofday(&tv, NULL);
289 gen.update_obj_bits(tv);
291 uint64_t e = utils::RDTSC();
295 return (g_prev_uuid = gen.finalize());
298 uint64_t gen_uuid64()
300 md5_hash h(gen_uuid());
301 return (static_cast<uint64_t>(h[0]) | (static_cast<uint64_t>(h[1]) << 32U)) ^
302 (static_cast<uint64_t>(h[3]) | (static_cast<uint64_t>(h[2]) << 20U));