xref: /freebsd/contrib/libfido2/fuzz/clock.c (revision 397e83df75e0fcd0d3fcb95ae4d794cb7600fc89)
1 /*
2  * Copyright (c) 2021 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <stdint.h>
9 #include <time.h>
10 
11 #include "mutator_aux.h"
12 
13 /*
14  * A pseudo-random monotonic clock with a probabilistic discontinuity to
15  * the end of time (as measured by struct timespec).
16  */
17 
18 extern int prng_up;
19 extern int __wrap_clock_gettime(clockid_t, struct timespec *);
20 extern int __real_clock_gettime(clockid_t, struct timespec *);
21 extern int __wrap_usleep(unsigned int);
22 static TLS struct timespec fuzz_clock;
23 
24 static void
25 tick(unsigned int usec)
26 {
27 	long long drift;
28 
29 	/*
30 	 * Simulate a jump to the end of time with 0.125% probability.
31 	 * This condition should be gracefully handled by callers of
32 	 * clock_gettime().
33 	 */
34 	if (uniform_random(800) < 1) {
35 		fuzz_clock.tv_sec = LLONG_MAX;
36 		fuzz_clock.tv_nsec = LONG_MAX;
37 		return;
38 	}
39 
40 	drift = usec * 1000LL + (long long)uniform_random(10000000); /* 10ms */
41 	if (LLONG_MAX - drift < (long long)fuzz_clock.tv_nsec) {
42 		fuzz_clock_reset(); /* Not much we can do here. */
43 	} else if (drift + (long long)fuzz_clock.tv_nsec < 1000000000) {
44 		fuzz_clock.tv_nsec += (long)(drift);
45 	} else {
46 		fuzz_clock.tv_sec  += (long)(drift / 1000000000);
47 		fuzz_clock.tv_nsec += (long)(drift % 1000000000);
48 	}
49 }
50 
51 int
52 __wrap_clock_gettime(clockid_t clk_id, struct timespec *tp)
53 {
54 	if (!prng_up || clk_id != CLOCK_MONOTONIC)
55 		return __real_clock_gettime(clk_id, tp);
56 	if (uniform_random(400) < 1)
57 		return -1;
58 
59 	tick(0);
60 	*tp = fuzz_clock;
61 
62 	return 0;
63 }
64 
65 int
66 __wrap_usleep(unsigned int usec)
67 {
68 	if (uniform_random(400) < 1)
69 		return -1;
70 
71 	tick(usec);
72 
73 	return 0;
74 }
75 
76 void
77 fuzz_clock_reset(void)
78 {
79 	memset(&fuzz_clock, 0, sizeof(fuzz_clock));
80 }
81