Home | History | Annotate | Line # | Download | only in fuzz
      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