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