[CRT] Massively improve performance of rand_s
[reactos.git] / ntoskrnl / ex / uuid.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/uuid.c
5 * PURPOSE: UUID generator
6 * PROGRAMMERS: Eric Kohl
7 * Thomas Weidenmueller
8 * Pierre Schweitzer
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17 #define SEED_BUFFER_SIZE 6
18
19 /* Number of 100ns ticks per clock tick. To be safe, assume that the clock
20 resolution is at least 1000 * 100 * (1/1000000) = 1/10 of a second */
21 #define TICKS_PER_CLOCK_TICK 1000
22 #define SECSPERDAY 86400
23 #define TICKSPERSEC 10000000
24
25 /* UUID system time starts at October 15, 1582 */
26 #define SECS_15_OCT_1582_TO_1601 ((17 + 30 + 31 + 365 * 18 + 5) * SECSPERDAY)
27 #define TICKS_15_OCT_1582_TO_1601 ((ULONGLONG)SECS_15_OCT_1582_TO_1601 * TICKSPERSEC)
28
29 /* 10000 in 100-ns model = 0.1 microsecond */
30 #define TIME_FRAME 10000
31
32 #if defined (ALLOC_PRAGMA)
33 #pragma alloc_text(INIT, ExpUuidInitialization)
34 #pragma alloc_text(INIT, ExLuidInitialization)
35 #endif
36
37
38 /* GLOBALS ****************************************************************/
39
40 FAST_MUTEX ExpUuidLock;
41 LARGE_INTEGER ExpUuidLastTimeAllocated;
42 ULONG ExpUuidSequenceNumber = 0;
43 BOOLEAN ExpUuidSequenceNumberValid;
44 BOOLEAN ExpUuidSequenceNumberNotSaved = FALSE;
45 UUID_CACHED_VALUES_STRUCT ExpUuidCachedValues = {0ULL, 0xFFFFFFFF, 0, 0, { 0x80, 0x6E, 0x6F, 0x6E, 0x69, 0x63}};
46 BOOLEAN ExpUuidCacheValid = FALSE;
47 ULONG ExpLuidIncrement = 1;
48 LARGE_INTEGER ExpLuid = {{0x3e9, 0x0}};
49
50 /* FUNCTIONS ****************************************************************/
51
52 /*
53 * @implemented
54 */
55 BOOLEAN
56 INIT_FUNCTION
57 NTAPI
58 ExpUuidInitialization(VOID)
59 {
60 ExInitializeFastMutex(&ExpUuidLock);
61
62 ExpUuidSequenceNumberValid = FALSE;
63 KeQuerySystemTime(&ExpUuidLastTimeAllocated);
64
65 return TRUE;
66 }
67
68
69 /*
70 * @implemented
71 */
72 #define VALUE_BUFFER_SIZE 20
73 static NTSTATUS
74 ExpUuidLoadSequenceNumber(PULONG Sequence)
75 {
76 UCHAR ValueBuffer[VALUE_BUFFER_SIZE];
77 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
78 OBJECT_ATTRIBUTES ObjectAttributes;
79 UNICODE_STRING KeyName, ValueName;
80 HANDLE KeyHandle;
81 ULONG ValueLength;
82 NTSTATUS Status;
83
84 PAGED_CODE();
85
86 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
87 RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber");
88
89 InitializeObjectAttributes(&ObjectAttributes,
90 &KeyName,
91 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
92 NULL,
93 NULL);
94 Status = ZwOpenKey(&KeyHandle, GENERIC_READ, &ObjectAttributes);
95 if (!NT_SUCCESS(Status))
96 {
97 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
98 return Status;
99 }
100
101 ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ValueBuffer;
102 Status = ZwQueryValueKey(KeyHandle,
103 &ValueName,
104 KeyValuePartialInformation,
105 ValueBuffer,
106 VALUE_BUFFER_SIZE,
107 &ValueLength);
108 ZwClose(KeyHandle);
109 if (!NT_SUCCESS(Status))
110 {
111 DPRINT("ZwQueryValueKey() failed (Status %lx)\n", Status);
112 return Status;
113 }
114
115 if (ValueInfo->Type != REG_DWORD || ValueInfo->DataLength != sizeof(DWORD))
116 {
117 return STATUS_UNSUCCESSFUL;
118 }
119
120 *Sequence = *((PULONG)ValueInfo->Data);
121
122 DPRINT("Loaded sequence %lx\n", *Sequence);
123
124 return STATUS_SUCCESS;
125 }
126 #undef VALUE_BUFFER_SIZE
127
128 /*
129 * @implemented
130 */
131 static NTSTATUS
132 ExpUuidSaveSequenceNumber(PULONG Sequence)
133 {
134 OBJECT_ATTRIBUTES ObjectAttributes;
135 UNICODE_STRING KeyName, ValueName;
136 HANDLE KeyHandle;
137 NTSTATUS Status;
138
139 PAGED_CODE();
140
141 RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Software\\Microsoft\\Rpc");
142 RtlInitUnicodeString(&ValueName, L"UuidSequenceNumber");
143
144 InitializeObjectAttributes(&ObjectAttributes,
145 &KeyName,
146 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
147 NULL,
148 NULL);
149 Status = ZwOpenKey(&KeyHandle,
150 GENERIC_READ | GENERIC_WRITE,
151 &ObjectAttributes);
152 if (!NT_SUCCESS(Status))
153 {
154 DPRINT("ZwOpenKey() failed (Status %lx)\n", Status);
155 return Status;
156 }
157
158 Status = ZwSetValueKey(KeyHandle,
159 &ValueName,
160 0,
161 REG_DWORD,
162 Sequence,
163 sizeof(ULONG));
164 ZwClose(KeyHandle);
165 if (!NT_SUCCESS(Status))
166 {
167 DPRINT("ZwSetValueKey() failed (Status %lx)\n", Status);
168 }
169
170 return Status;
171 }
172
173 /*
174 * @implemented
175 * Warning! This function must be called
176 * with ExpUuidLock held!
177 */
178 static VOID
179 ExpUuidSaveSequenceNumberIf(VOID)
180 {
181 NTSTATUS Status;
182
183 PAGED_CODE();
184
185 /* Only save sequence if it has to */
186 if (ExpUuidSequenceNumberNotSaved == TRUE)
187 {
188 Status = ExpUuidSaveSequenceNumber(&ExpUuidSequenceNumber);
189 if (NT_SUCCESS(Status))
190 {
191 ExpUuidSequenceNumberNotSaved = FALSE;
192 }
193 }
194 }
195
196 /*
197 * @implemented
198 * Warning! This function must be called
199 * with ExpUuidLock held!
200 */
201 static NTSTATUS
202 ExpAllocateUuids(PULARGE_INTEGER Time,
203 PULONG Range,
204 PULONG Sequence)
205 {
206 NTSTATUS Status;
207 LARGE_INTEGER Counter, Frequency, CurrentTime, TimeDiff, ClockDiff;
208
209 PAGED_CODE();
210
211 /* Initialize sequence number */
212 if (!ExpUuidSequenceNumberValid)
213 {
214 /* Try to load sequence number */
215 Status = ExpUuidLoadSequenceNumber(&ExpUuidSequenceNumber);
216 if (NT_SUCCESS(Status))
217 {
218 ++ExpUuidSequenceNumber;
219 }
220 else
221 {
222 /* If we cannot, generate a "true" random */
223 Counter = KeQueryPerformanceCounter(&Frequency);
224 ExpUuidSequenceNumber ^= (ULONG_PTR)&Status ^ (ULONG_PTR)Sequence ^ Counter.LowPart ^ Counter.HighPart;
225 }
226
227 /* It's valid and to be saved */
228 ExpUuidSequenceNumberValid = TRUE;
229 ExpUuidSequenceNumberNotSaved = TRUE;
230 }
231
232 KeQuerySystemTime(&CurrentTime);
233 TimeDiff.QuadPart = CurrentTime.QuadPart - ExpUuidLastTimeAllocated.QuadPart;
234 /* If time went backwards, change sequence (see RFC example) */
235 if (TimeDiff.QuadPart < 0)
236 {
237 ++ExpUuidSequenceNumber;
238 TimeDiff.QuadPart = 2 * TIME_FRAME;
239
240 /* It's to be saved */
241 ExpUuidSequenceNumberNotSaved = TRUE;
242 ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - 2 * TIME_FRAME;
243 }
244
245 if (TimeDiff.QuadPart == 0)
246 {
247 return STATUS_RETRY;
248 }
249
250 /* If time diff > 0.1ms, squash it to reduce it to keep our clock resolution */
251 if (TimeDiff.HighPart > 0 || TimeDiff.QuadPart > TICKS_PER_CLOCK_TICK * TIME_FRAME)
252 {
253 TimeDiff.QuadPart = TICKS_PER_CLOCK_TICK * TIME_FRAME;
254 }
255
256 if (TimeDiff.HighPart < 0 || TimeDiff.QuadPart <= TIME_FRAME)
257 {
258 *Range = TimeDiff.QuadPart;
259 ClockDiff.QuadPart = 0LL;
260 }
261 else
262 {
263 *Range = TIME_FRAME;
264 ClockDiff.QuadPart = TimeDiff.QuadPart - TIME_FRAME;
265 --ClockDiff.HighPart;
266 }
267
268 Time->QuadPart = CurrentTime.QuadPart - *Range - ClockDiff.QuadPart;
269 ExpUuidLastTimeAllocated.QuadPart = CurrentTime.QuadPart - ClockDiff.QuadPart;
270 *Sequence = ExpUuidSequenceNumber;
271
272 return STATUS_SUCCESS;
273 }
274
275 /*
276 * @implemented
277 * Warning! This function must be called
278 * with ExpUuidLock held!
279 */
280 static NTSTATUS
281 ExpUuidGetValues(PUUID_CACHED_VALUES_STRUCT CachedValues)
282 {
283 NTSTATUS Status;
284 ULARGE_INTEGER Time;
285 ULONG Range;
286 ULONG Sequence;
287
288 PAGED_CODE();
289
290 /* Allocate UUIDs */
291 Status = ExpAllocateUuids(&Time, &Range, &Sequence);
292 if (Status == STATUS_RETRY)
293 {
294 return Status;
295 }
296
297 if (!NT_SUCCESS(Status))
298 {
299 return STATUS_NO_MEMORY;
300 }
301
302 /* We need at least one UUID */
303 ASSERT(Range != 0);
304
305 /* Set up our internal cache
306 * See format_uuid_v1 in RFC4122 for magic values
307 */
308 CachedValues->ClockSeqLow = Sequence;
309 CachedValues->ClockSeqHiAndReserved = (Sequence & 0x3F00) >> 8;
310 CachedValues->ClockSeqHiAndReserved |= 0x80;
311 CachedValues->AllocatedCount = Range;
312
313 /*
314 * Time is relative to UUID time
315 * And we set last time range for all the possibly
316 * returnable UUID
317 */
318 Time.QuadPart += TICKS_15_OCT_1582_TO_1601;
319 CachedValues->Time = Time.QuadPart + (Range - 1);
320
321 return STATUS_SUCCESS;
322 }
323
324 /*
325 * @implemented
326 */
327 BOOLEAN
328 INIT_FUNCTION
329 NTAPI
330 ExLuidInitialization(VOID)
331 {
332 return TRUE;
333 }
334
335 /*
336 * @implemented
337 */
338 VOID
339 NTAPI
340 ExAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
341 {
342 /* Atomically increment the luid */
343 *(LONG64*)LocallyUniqueId = InterlockedExchangeAdd64(&ExpLuid.QuadPart,
344 ExpLuidIncrement);
345 }
346
347
348 /*
349 * @implemented
350 */
351 NTSTATUS
352 NTAPI
353 NtAllocateLocallyUniqueId(OUT LUID *LocallyUniqueId)
354 {
355 KPROCESSOR_MODE PreviousMode;
356 PAGED_CODE();
357
358 /* Probe if user mode */
359 PreviousMode = ExGetPreviousMode();
360 if (PreviousMode != KernelMode)
361 {
362 _SEH2_TRY
363 {
364 ProbeForWrite(LocallyUniqueId,
365 sizeof(LUID),
366 sizeof(ULONG));
367 }
368 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
369 {
370 _SEH2_YIELD(return _SEH2_GetExceptionCode());
371 }
372 _SEH2_END;
373 }
374
375 /* Do the allocation */
376 ExAllocateLocallyUniqueId(LocallyUniqueId);
377 return STATUS_SUCCESS;
378 }
379
380 /*
381 * @implemented
382 */
383 NTSTATUS
384 NTAPI
385 ExUuidCreate(OUT UUID *Uuid)
386 {
387 NTSTATUS Status;
388 LONG AllocatedCount;
389 LARGE_INTEGER Time;
390 BOOLEAN Valid;
391
392 PAGED_CODE();
393
394 Status = STATUS_SUCCESS;
395 /* Loop until we have an UUID to return */
396 while (TRUE)
397 {
398 /* Try to gather node values */
399 do
400 {
401 Time.QuadPart = ExpUuidCachedValues.Time;
402
403 RtlCopyMemory(&Uuid->Data4[0],
404 &ExpUuidCachedValues.NodeId[0],
405 SEED_BUFFER_SIZE);
406 Valid = ExpUuidCacheValid;
407 AllocatedCount = InterlockedDecrement(&ExpUuidCachedValues.AllocatedCount);
408 }
409 /* Loop till we can do it without being disturbed */
410 while (Time.QuadPart != ExpUuidCachedValues.Time);
411
412 /* We have more than an allocated UUID left, that's OK to return! */
413 if (AllocatedCount >= 0)
414 {
415 break;
416 }
417
418 /*
419 * Here, we're out of UUIDs, we need to allocate more
420 * We need to be alone to do it, so lock the mutex
421 */
422 ExAcquireFastMutex(&ExpUuidLock);
423 if (Time.QuadPart == ExpUuidCachedValues.Time)
424 {
425 /* If allocation fails, bail out! */
426 Status = ExpUuidGetValues(&ExpUuidCachedValues);
427 if (Status != STATUS_SUCCESS)
428 {
429 ExReleaseFastMutex(&ExpUuidLock);
430 return Status;
431 }
432
433 /* Save our current sequence if changed */
434 ExpUuidSaveSequenceNumberIf();
435 }
436 ExReleaseFastMutex(&ExpUuidLock);
437 }
438
439 /*
440 * Once here, we've got an UUID to return
441 * But, if our init wasn't sane, then, make
442 * sure it's only used locally
443 */
444 if (!Valid)
445 {
446 Status = RPC_NT_UUID_LOCAL_ONLY;
447 }
448
449 /* Set our timestamp - see RFC4211 */
450 Time.QuadPart -= AllocatedCount;
451 Uuid->Data1 = Time.LowPart;
452 Uuid->Data2 = Time.HighPart;
453 /* We also set the bit for GUIDv1 */
454 Uuid->Data3 = ((Time.HighPart >> 16) & 0x0FFF) | 0x1000;
455
456 return Status;
457 }
458
459 /*
460 * @implemented
461 */
462 NTSTATUS
463 NTAPI
464 NtAllocateUuids(OUT PULARGE_INTEGER Time,
465 OUT PULONG Range,
466 OUT PULONG Sequence,
467 OUT PUCHAR Seed)
468 {
469 ULARGE_INTEGER IntTime;
470 ULONG IntRange, IntSequence;
471 NTSTATUS Status;
472 KPROCESSOR_MODE PreviousMode;
473
474 PAGED_CODE();
475
476 /* Probe if user mode */
477 PreviousMode = ExGetPreviousMode();
478 if (PreviousMode != KernelMode)
479 {
480 _SEH2_TRY
481 {
482 ProbeForWrite(Time,
483 sizeof(ULARGE_INTEGER),
484 sizeof(ULONG));
485
486 ProbeForWrite(Range,
487 sizeof(ULONG),
488 sizeof(ULONG));
489
490 ProbeForWrite(Sequence,
491 sizeof(ULONG),
492 sizeof(ULONG));
493
494 ProbeForWrite(Seed,
495 SEED_BUFFER_SIZE,
496 sizeof(UCHAR));
497 }
498 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
499 {
500 _SEH2_YIELD(return _SEH2_GetExceptionCode());
501 }
502 _SEH2_END;
503 }
504
505 /* During allocation we must be alone */
506 ExAcquireFastMutex(&ExpUuidLock);
507
508 Status = ExpAllocateUuids(&IntTime,
509 &IntRange,
510 &IntSequence);
511 if (!NT_SUCCESS(Status))
512 {
513 ExReleaseFastMutex(&ExpUuidLock);
514 return Status;
515 }
516
517 /* If sequence number was changed, save it */
518 ExpUuidSaveSequenceNumberIf();
519
520 /* Allocation done, so we can release */
521 ExReleaseFastMutex(&ExpUuidLock);
522
523 /* Write back UUIDs to caller */
524 _SEH2_TRY
525 {
526 Time->QuadPart = IntTime.QuadPart;
527 *Range = IntRange;
528 *Sequence = IntSequence;
529
530 RtlCopyMemory(Seed,
531 &ExpUuidCachedValues.NodeId[0],
532 SEED_BUFFER_SIZE);
533
534 Status = STATUS_SUCCESS;
535 }
536 _SEH2_EXCEPT(ExSystemExceptionFilter())
537 {
538 Status = _SEH2_GetExceptionCode();
539 }
540 _SEH2_END;
541
542 return Status;
543 }
544
545
546 /*
547 * @implemented
548 */
549 NTSTATUS
550 NTAPI
551 NtSetUuidSeed(IN PUCHAR Seed)
552 {
553 NTSTATUS Status;
554 BOOLEAN GotContext;
555 PACCESS_TOKEN Token;
556 SECURITY_SUBJECT_CONTEXT SubjectContext;
557 LUID CallerLuid, SystemLuid = SYSTEM_LUID;
558
559 PAGED_CODE();
560
561 /* Should only be done by umode */
562 ASSERT(KeGetPreviousMode() != KernelMode);
563
564 /* No context to release */
565 GotContext = FALSE;
566 _SEH2_TRY
567 {
568 /* Get our caller context and remember to release it */
569 SeCaptureSubjectContext(&SubjectContext);
570 GotContext = TRUE;
571
572 /* Get caller access token and its associated ID */
573 Token = SeQuerySubjectContextToken(&SubjectContext);
574 Status = SeQueryAuthenticationIdToken(Token, &CallerLuid);
575 if (!NT_SUCCESS(Status))
576 {
577 RtlRaiseStatus(Status);
578 }
579
580 /* This call is only allowed for SYSTEM */
581 if (!RtlEqualLuid(&CallerLuid, &SystemLuid))
582 {
583 RtlRaiseStatus(STATUS_ACCESS_DENIED);
584 }
585
586 /* Check for buffer validity and then copy it to our seed */
587 ProbeForRead(Seed, SEED_BUFFER_SIZE, sizeof(UCHAR));
588 RtlCopyMemory(&ExpUuidCachedValues.NodeId[0], Seed, SEED_BUFFER_SIZE);
589
590 /*
591 * According to RFC 4122, UUID seed is based on MAC addresses
592 * If it is randomly set, then, it must have its multicast be set
593 * to be valid and avoid collisions
594 * Reflect it here
595 */
596 ExpUuidCacheValid = ~(*Seed >> 7) & 1;
597
598 Status = STATUS_SUCCESS;
599 }
600 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
601 {
602 Status = _SEH2_GetExceptionCode();
603 }
604 _SEH2_END;
605
606 /* Release context if required */
607 if (GotContext)
608 {
609 SeReleaseSubjectContext(&SubjectContext);
610 }
611
612 return Status;
613 }
614
615 /* EOF */