Timing unit tests: more protection against infinite loops

If timing_timer_simple fails because it detects that timers are likely
to never expire (e.g. going backward or not incrementing), skip all
tests that rely on timers.
This commit is contained in:
Gilles Peskine 2017-10-18 20:00:32 +02:00
parent 078f1a1512
commit 2a26d620fb

View File

@ -38,6 +38,14 @@ static int expected_delay_status( uint32_t int_ms, uint32_t fin_ms,
0 ); 0 );
} }
/* Some conditions in timing_timer_simple suggest that timers are unreliable.
Most other test cases rely on timers to terminate, and could loop
indefinitely if timers are too broken. So if timing_timer_simple detected a
timer that risks not terminating (going backwards, or not reaching the
desired count in the alloted clock cycles), set this flag to immediately
fail those other tests without running any timers. */
static int timers_are_badly_broken = 0;
/* END_HEADER */ /* END_HEADER */
/* BEGIN_DEPENDENCIES /* BEGIN_DEPENDENCIES
@ -73,6 +81,15 @@ void timing_timer_simple( )
return; return;
exit: exit:
if( iterations >= TIMING_SHORT_TEST_ITERATIONS_MAX ||
new_millis < millis )
{
/* The timer was very unreliable: it didn't increment and the loop ran
out, or it went backwards. Other tests that use timers might go
into an infinite loop, so we'll skip them. */
timers_are_badly_broken = 1;
}
/* No cleanup needed, but show some diagnostic iterations, because timing /* No cleanup needed, but show some diagnostic iterations, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
mbedtls_fprintf( stdout, " Finished with millis=%lu new_millis=%lu get(timer)<=%lu iterations=%lu\n", mbedtls_fprintf( stdout, " Finished with millis=%lu new_millis=%lu get(timer)<=%lu iterations=%lu\n",
@ -87,6 +104,11 @@ void timing_timer_reset( )
struct mbedtls_timing_hr_time timer; struct mbedtls_timing_hr_time timer;
unsigned long millis = 0; unsigned long millis = 0;
unsigned long iterations = 0; unsigned long iterations = 0;
/* Skip this test if it looks like timers don't work at all, to avoid an
infinite loop below. */
TEST_ASSERT( !timers_are_badly_broken );
/* Start the timer. Timers are always reset to 0. */ /* Start the timer. Timers are always reset to 0. */
TEST_ASSERT( mbedtls_timing_get_timer( &timer, 1 ) == 0 ); TEST_ASSERT( mbedtls_timing_get_timer( &timer, 1 ) == 0 );
/* Busy-wait loop for a few milliseconds */ /* Busy-wait loop for a few milliseconds */
@ -107,6 +129,7 @@ void timing_timer_reset( )
exit: exit:
/* No cleanup needed, but show some diagnostic information, because timing /* No cleanup needed, but show some diagnostic information, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with millis=%lu get(timer)<=%lu iterations=%lu\n", mbedtls_fprintf( stdout, " Finished with millis=%lu get(timer)<=%lu iterations=%lu\n",
millis, mbedtls_timing_get_timer( &timer, 0 ), millis, mbedtls_timing_get_timer( &timer, 0 ),
iterations ); iterations );
@ -117,7 +140,11 @@ exit:
void timing_two_timers( int delta ) void timing_two_timers( int delta )
{ {
struct mbedtls_timing_hr_time timer1, timer2; struct mbedtls_timing_hr_time timer1, timer2;
unsigned long millis1, millis2; unsigned long millis1 = 0, millis2 = 0;
/* Skip this test if it looks like timers don't work at all, to avoid an
infinite loop below. */
TEST_ASSERT( !timers_are_badly_broken );
/* Start the first timer and wait for a short time. */ /* Start the first timer and wait for a short time. */
(void) mbedtls_timing_get_timer( &timer1, 1 ); (void) mbedtls_timing_get_timer( &timer1, 1 );
@ -153,6 +180,7 @@ void timing_two_timers( int delta )
exit: exit:
/* No cleanup needed, but show some diagnostic iterations, because timing /* No cleanup needed, but show some diagnostic iterations, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with millis1=%lu get(timer1)<=%lu millis2=%lu get(timer2)<=%lu\n", mbedtls_fprintf( stdout, " Finished with millis1=%lu get(timer1)<=%lu millis2=%lu get(timer2)<=%lu\n",
millis1, mbedtls_timing_get_timer( &timer1, 0 ), millis1, mbedtls_timing_get_timer( &timer1, 0 ),
millis2, mbedtls_timing_get_timer( &timer2, 0 ) ); millis2, mbedtls_timing_get_timer( &timer2, 0 ) );
@ -177,6 +205,10 @@ void timing_alarm( int seconds )
TIMING_ALARM_0_DELAY_MS ); TIMING_ALARM_0_DELAY_MS );
unsigned long iterations = 0; unsigned long iterations = 0;
/* Skip this test if it looks like timers don't work at all, to avoid an
infinite loop below. */
TEST_ASSERT( !timers_are_badly_broken );
/* Set an alarm and count how long it takes with a timer. */ /* Set an alarm and count how long it takes with a timer. */
(void) mbedtls_timing_get_timer( &timer, 1 ); (void) mbedtls_timing_get_timer( &timer, 1 );
mbedtls_set_alarm( seconds ); mbedtls_set_alarm( seconds );
@ -209,6 +241,7 @@ void timing_alarm( int seconds )
exit: exit:
/* Show some diagnostic iterations, because timing /* Show some diagnostic iterations, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with alarmed=%d millis=%lu get(timer)<=%lu iterations=%lu\n", mbedtls_fprintf( stdout, " Finished with alarmed=%d millis=%lu get(timer)<=%lu iterations=%lu\n",
mbedtls_timing_alarmed, mbedtls_timing_alarmed,
millis, mbedtls_timing_get_timer( &timer, 0 ), millis, mbedtls_timing_get_timer( &timer, 0 ),
@ -228,7 +261,7 @@ void timing_delay( int int_ms, int fin_ms )
mbedtls_timing_delay_context delay; mbedtls_timing_delay_context delay;
struct mbedtls_timing_hr_time timer; struct mbedtls_timing_hr_time timer;
unsigned long delta; /* delay started between timer=0 and timer=delta */ unsigned long delta = 0; /* delay started between timer=0 and timer=delta */
unsigned long before = 0, after = 0; unsigned long before = 0, after = 0;
unsigned long iterations = 0; unsigned long iterations = 0;
int status = -2; int status = -2;
@ -238,6 +271,10 @@ void timing_delay( int int_ms, int fin_ms )
assert( int_ms >= 0 ); assert( int_ms >= 0 );
assert( fin_ms >= 0 ); assert( fin_ms >= 0 );
/* Skip this test if it looks like timers don't work at all, to avoid an
infinite loop below. */
TEST_ASSERT( !timers_are_badly_broken );
/* Start a reference timer. Program a delay, and verify that the status of /* Start a reference timer. Program a delay, and verify that the status of
the delay is consistent with the time given by the reference timer. */ the delay is consistent with the time given by the reference timer. */
(void) mbedtls_timing_get_timer( &timer, 1 ); (void) mbedtls_timing_get_timer( &timer, 1 );
@ -310,6 +347,7 @@ void timing_delay( int int_ms, int fin_ms )
exit: exit:
/* No cleanup needed, but show some diagnostic iterations, because timing /* No cleanup needed, but show some diagnostic iterations, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with delta=%lu before=%lu after=%lu status=%d iterations=%lu\n", mbedtls_fprintf( stdout, " Finished with delta=%lu before=%lu after=%lu status=%d iterations=%lu\n",
delta, before, after, status, iterations ); delta, before, after, status, iterations );
if( warn_inconclusive ) if( warn_inconclusive )
@ -326,7 +364,11 @@ void timing_hardclock( )
completely nonsensical values. */ completely nonsensical values. */
struct mbedtls_timing_hr_time timer; struct mbedtls_timing_hr_time timer;
unsigned long hardclock0, hardclock1, delta1; unsigned long hardclock0 = -1, hardclock1 = -1, delta1 = -1;
/* Skip this test if it looks like timers don't work at all, to avoid an
infinite loop below. */
TEST_ASSERT( !timers_are_badly_broken );
hardclock0 = mbedtls_timing_hardclock( ); hardclock0 = mbedtls_timing_hardclock( );
/* Wait 2ms to ensure a nonzero delay. Since the timer interface has 1ms /* Wait 2ms to ensure a nonzero delay. Since the timer interface has 1ms
@ -354,6 +396,7 @@ void timing_hardclock( )
exit: exit:
/* No cleanup needed, but show some diagnostic iterations, because timing /* No cleanup needed, but show some diagnostic iterations, because timing
problems can be hard to reproduce. */ problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with hardclock=%lu,%lu\n", mbedtls_fprintf( stdout, " Finished with hardclock=%lu,%lu\n",
hardclock0, hardclock1 ); hardclock0, hardclock1 );
} }