]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_uuid.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_uuid.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
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
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
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"
33
34 #include <unistd.h>
35 #include <sys/syscall.h>
36 #include <sys/types.h>
37 #include <sys/time.h>
38 #include <time.h>
39 #include <pwd.h>
40
41 namespace vogl
42 {
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()
46     {
47         static uint64_t s_counter;
48
49         // Get as much entropy as we can here
50
51         const uint N = 2;
52         void *p[N];
53         memset(p, 0, sizeof(p));
54
55         md5_hash_gen gen;
56         timer_ticks tick_hist[N];
57         for (uint i = 0; i < N; i++)
58         {
59             uint64_t start_rdtsc = utils::RDTSC();
60             gen.update(start_rdtsc);
61
62             gen.update(s_counter);
63             gen.update((uint64_t) & s_counter);
64             s_counter++;
65
66             // Hash stack address of gen_uuid
67             gen.update((uint64_t) & gen_uuid);
68
69             // Hash the initial timer ticks, and time(NULL)
70             gen.update(timer::get_init_ticks());
71             gen.update((uint64_t)time(NULL));
72
73             // Hash user ID, name, shell, home dir
74             uid_t uid = geteuid();
75             gen.update(uid);
76             struct passwd *pw = getpwuid(uid);
77             gen.update((uint64_t) & pw);
78             if (pw)
79             {
80                 gen.update(pw, sizeof(struct passwd));
81                 if (pw->pw_name)
82                     gen.update(pw->pw_name, vogl_strlen(pw->pw_name));
83                 if (pw->pw_passwd)
84                     gen.update(pw->pw_passwd, vogl_strlen(pw->pw_passwd));
85                 if (pw->pw_shell)
86                     gen.update(pw->pw_shell, vogl_strlen(pw->pw_shell));
87                 if (pw->pw_dir)
88                     gen.update(pw->pw_dir, vogl_strlen(pw->pw_dir));
89                 if (pw->pw_gecos)
90                     gen.update(pw->pw_gecos, vogl_strlen(pw->pw_gecos));
91             }
92
93             uint8_vec buf;
94
95             timer_ticks ticks = timer::get_ticks();
96             gen.update(ticks);
97
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).
100             if (!i)
101             {
102                 uint64_t st = utils::RDTSC();
103                 timer tm;
104                 tm.start();
105
106                 const char *pFilename = "!_!_!_!_!_!_!_vogl_temp!_!_!_!_!_!_!_!_.txt";
107                 FILE *pFile = vogl_fopen(pFilename, "wb");
108                 gen.update_obj_bits(pFile);
109                 if (pFile)
110                 {
111                     fwrite("X", 1, 1, pFile);
112                     fflush(pFile);
113                     fsync(fileno(pFile));
114                     vogl_fclose(pFile);
115                     remove(pFilename);
116                 }
117
118                 uint64_t t = utils::RDTSC() - st;
119                 gen.update(t);
120
121                 tm.stop();
122
123                 gen.update(tm.get_elapsed_ticks());
124             }
125
126             // Grab some bits from /dev/urandom (not /dev/random - it may block for a long time)
127             {
128                 const uint N = 64;
129                 char buf[N];
130                 FILE *fp = vogl_fopen("/dev/urandom", "rb");
131                 gen.update_obj_bits(fp);
132                 if (fp)
133                 {
134                     size_t n = fread(buf, 1, N, fp);
135                     VOGL_NOTE_UNUSED(n);
136                     vogl_fclose(fp);
137
138                     gen.update(buf, sizeof(buf));
139                 }
140             }
141
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)                                               \
145     do                                                                    \
146     {                                                                     \
147         bool success = cfile_stream::read_file_into_array(filename, buf); \
148         gen.update_obj_bits(success);                                     \
149         gen.update(buf);                                                  \
150     } while (0)
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");
178 #undef HASH_FILE
179
180             gen.update(utils::RDTSC());
181
182             // Hash thread, process ID's, etc.
183             pid_t tid = (pid_t)syscall(SYS_gettid);
184             gen.update_obj_bits(tid);
185
186             pid_t pid = getpid();
187             gen.update_obj_bits(pid);
188
189             pid = getppid();
190             gen.update_obj_bits(pid);
191             gen.update((uint64_t) & pid);
192
193             ticks -= timer::get_ticks();
194             tick_hist[i] = ticks;
195             gen.update(ticks);
196
197             ticks = timer::get_ticks();
198
199             // Get some entropy from the stack.
200             char purposely_uninitialized_buf[256];
201             gen.update(purposely_uninitialized_buf, sizeof(purposely_uninitialized_buf));
202
203             // Get some entropy from the heap.
204             p[i] = vogl_malloc(65536 * (i + 1));
205             gen.update_obj_bits(p[i]);
206             if (p[i])
207             {
208                 for (uint j = 0; j < 16; j++)
209                     gen.update_obj_bits(reinterpret_cast<const uint64_t *>(p)[j]);
210             }
211
212             struct timeval tv;
213             gettimeofday(&tv, NULL);
214             gen.update_obj_bits(tv);
215
216             // Hash the current environment
217             uint e = 0;
218             while (environ[e])
219             {
220                 gen.update(environ[e], vogl_strlen(environ[e]));
221                 ++e;
222             }
223
224             uint64_t s = utils::RDTSC();
225
226             // Try to get some entropy from the scheduler.
227             vogl_sleep(2);
228
229             gen.update(utils::RDTSC() - s);
230
231             ticks -= timer::get_ticks();
232             gen.update(ticks);
233
234             gen.update(utils::RDTSC() - start_rdtsc);
235         }
236
237         for (uint i = 1; i < N; i++)
238         {
239             uint64_t t = tick_hist[i] - tick_hist[i - 1];
240             gen.update(t);
241         }
242
243         for (uint i = 0; i < N; i++)
244             vogl_free(p[i]);
245
246         return gen.finalize();
247     }
248
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;
254
255     md5_hash gen_uuid()
256     {
257         uint64_t s = utils::RDTSC();
258
259         scoped_mutex scoped_lock(g_uuid_mutex);
260
261         if (!g_uuid_initialized)
262         {
263             g_uuid_key = init_uuid();
264
265             g_uuid_rand.seed(g_uuid_key);
266
267             g_uuid_initialized = true;
268         }
269
270         uint32 h[4] = { g_uuid_rand.urand32(), g_uuid_rand.urand32(), g_uuid_rand.urand32(), g_uuid_rand.urand32() };
271
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);
276
277         gen.update(s);
278         gen.update((uint64_t) & s);
279
280         uint64_t purposely_uninitialized_variable;
281         gen.update(&purposely_uninitialized_variable, sizeof(purposely_uninitialized_variable));
282
283         static uint64_t s_counter;
284         s_counter++;
285         gen.update(s_counter);
286
287         struct timeval tv;
288         gettimeofday(&tv, NULL);
289         gen.update_obj_bits(tv);
290
291         uint64_t e = utils::RDTSC();
292
293         gen.update(e - s);
294
295         return (g_prev_uuid = gen.finalize());
296     }
297
298     uint64_t gen_uuid64()
299     {
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));
303     }
304
305 } // namespace vogl