Merge branch 'development' into development-restricted

* development:
  Timing self test: shorten redundant tests
  Timing self test: increased duration
  Timing self test: increased tolerance
  Timing unit tests: more protection against infinite loops
  Unit test for mbedtls_timing_hardclock
  New timing unit tests
  selftest: allow excluding a subset of the tests
  selftest: allow running a subset of the tests
  selftest: refactor to separate the list of tests from the logic
  Timing self test: print some diagnosis information
  mbedtls_timing_get_timer: don't use uninitialized memory
  timing interface documentation: minor clarifications
  Timing: fix mbedtls_set_alarm(0) on Unix/POSIX
This commit is contained in:
Manuel Pégourié-Gonnard 2017-12-26 10:42:50 +01:00
commit eb2a6ab518
6 changed files with 722 additions and 317 deletions

View file

@ -41,6 +41,14 @@ Security
Features
* Allow comments in test data files.
* The selftest program can execute a subset of the tests based on command
line arguments.
* New unit tests for timing. Improve the self-test to be more robust
when run on a heavily-loaded machine.
New deprecations
* Deprecate usage of RSA primitives with non-matching key-type
(e.g., signing with a public key).
Bugfix
* Fix ssl_parse_record_header() to silently discard invalid DTLS records
@ -87,16 +95,14 @@ Bugfix
fragile yet non-exploitable code-paths.
* Fix crash when calling mbedtls_ssl_cache_free() twice. Found by
MilenkoMitrovic, #1104
* Fix mbedtls_timing_alarm(0) on Unix.
* Fix use of uninitialized memory in mbedtls_timing_get_timer when reset=1.
Changes
* Extend cert_write example program by options to set the CRT version
and the message digest. Further, allow enabling/disabling of authority
identifier, subject identifier and basic constraints extensions.
New deprecations
* Deprecate usage of RSA primitives with non-matching key-type
(e.g., signing with a public key).
= mbed TLS 2.6.0 branch released 2017-08-10
Security

View file

@ -1,7 +1,7 @@
/**
* \file timing.h
*
* \brief Portable interface to the CPU cycle counter
* \brief Portable interface to timeouts and to the CPU cycle counter
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
@ -65,6 +65,9 @@ extern volatile int mbedtls_timing_alarmed;
* \warning This is only a best effort! Do not rely on this!
* In particular, it is known to be unreliable on virtual
* machines.
*
* \note This value starts at an unspecified origin and
* may wrap around.
*/
unsigned long mbedtls_timing_hardclock( void );
@ -72,7 +75,18 @@ unsigned long mbedtls_timing_hardclock( void );
* \brief Return the elapsed time in milliseconds
*
* \param val points to a timer structure
* \param reset if set to 1, the timer is restarted
* \param reset If 0, query the elapsed time. Otherwise (re)start the timer.
*
* \return Elapsed time since the previous reset in ms. When
* restarting, this is always 0.
*
* \note To initialize a timer, call this function with reset=1.
*
* Determining the elapsed time and resetting the timer is not
* atomic on all platforms, so after the sequence
* `{ get_timer(1); ...; time1 = get_timer(1); ...; time2 =
* get_timer(0) }` the value time1+time2 is only approximately
* the delay since the first reset.
*/
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset );
@ -80,6 +94,7 @@ unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int
* \brief Setup an alarm clock
*
* \param seconds delay before the "mbedtls_timing_alarmed" flag is set
* (must be >=0)
*
* \warning Only one alarm at a time is supported. In a threaded
* context, this means one for the whole process, not one per
@ -91,11 +106,15 @@ void mbedtls_set_alarm( int seconds );
* \brief Set a pair of delays to watch
* (See \c mbedtls_timing_get_delay().)
*
* \param data Pointer to timing data
* \param data Pointer to timing data.
* Must point to a valid \c mbedtls_timing_delay_context struct.
* \param int_ms First (intermediate) delay in milliseconds.
* The effect if int_ms > fin_ms is unspecified.
* \param fin_ms Second (final) delay in milliseconds.
* Pass 0 to cancel the current delay.
*
* \note To set a single delay, either use \c mbedtls_timing_set_timer
* directly or use this function with int_ms == fin_ms.
*/
void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms );
@ -106,7 +125,7 @@ void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms );
* \param data Pointer to timing data
* Must point to a valid \c mbedtls_timing_delay_context struct.
*
* \return -1 if cancelled (fin_ms = 0)
* \return -1 if cancelled (fin_ms = 0),
* 0 if none of the delays are passed,
* 1 if only the intermediate delay is passed,
* 2 if the final delay is passed.

View file

@ -244,21 +244,23 @@ volatile int mbedtls_timing_alarmed = 0;
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
{
unsigned long delta;
LARGE_INTEGER offset, hfreq;
struct _hr_time *t = (struct _hr_time *) val;
QueryPerformanceCounter( &offset );
QueryPerformanceFrequency( &hfreq );
delta = (unsigned long)( ( 1000 *
( offset.QuadPart - t->start.QuadPart ) ) /
hfreq.QuadPart );
if( reset )
{
QueryPerformanceCounter( &t->start );
return( delta );
return( 0 );
}
else
{
unsigned long delta;
LARGE_INTEGER now, hfreq;
QueryPerformanceCounter( &now );
QueryPerformanceFrequency( &hfreq );
delta = (unsigned long)( ( now.QuadPart - t->start.QuadPart ) * 1000ul
/ hfreq.QuadPart );
return( delta );
}
}
/* It's OK to use a global because alarm() is supposed to be global anyway */
@ -285,23 +287,22 @@ void mbedtls_set_alarm( int seconds )
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
{
unsigned long delta;
struct timeval offset;
struct _hr_time *t = (struct _hr_time *) val;
gettimeofday( &offset, NULL );
if( reset )
{
t->start.tv_sec = offset.tv_sec;
t->start.tv_usec = offset.tv_usec;
gettimeofday( &t->start, NULL );
return( 0 );
}
delta = ( offset.tv_sec - t->start.tv_sec ) * 1000
+ ( offset.tv_usec - t->start.tv_usec ) / 1000;
return( delta );
else
{
unsigned long delta;
struct timeval now;
gettimeofday( &now, NULL );
delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul
+ ( now.tv_usec - t->start.tv_usec ) / 1000;
return( delta );
}
}
static void sighandler( int signum )
@ -315,6 +316,12 @@ void mbedtls_set_alarm( int seconds )
mbedtls_timing_alarmed = 0;
signal( SIGALRM, sighandler );
alarm( seconds );
if( seconds == 0 )
{
/* alarm(0) cancelled any previous pending alarm, but the
handler won't fire, so raise the flag straight away. */
mbedtls_timing_alarmed = 1;
}
}
#endif /* _WIN32 && !EFIX64 && !EFI32 */
@ -378,13 +385,21 @@ static void busy_msleep( unsigned long msec )
(void) j;
}
#define FAIL do \
{ \
if( verbose != 0 ) \
mbedtls_printf( "failed\n" ); \
\
return( 1 ); \
} while( 0 )
#define FAIL do \
{ \
if( verbose != 0 ) \
{ \
mbedtls_printf( "failed at line %d\n", __LINE__ ); \
mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \
cycles, ratio, millisecs, secs, hardfail, \
(unsigned long) a, (unsigned long) b ); \
mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \
mbedtls_timing_get_timer( &hires, 0 ), \
mbedtls_timing_get_timer( &ctx.timer, 0 ), \
mbedtls_timing_get_delay( &ctx ) ); \
} \
return( 1 ); \
} while( 0 )
/*
* Checkup routine
@ -394,22 +409,22 @@ static void busy_msleep( unsigned long msec )
*/
int mbedtls_timing_self_test( int verbose )
{
unsigned long cycles, ratio;
unsigned long millisecs, secs;
int hardfail;
unsigned long cycles = 0, ratio = 0;
unsigned long millisecs = 0, secs = 0;
int hardfail = 0;
struct mbedtls_timing_hr_time hires;
uint32_t a, b;
uint32_t a = 0, b = 0;
mbedtls_timing_delay_context ctx;
if( verbose != 0 )
mbedtls_printf( " TIMING tests note: will take some time!\n" );
if( verbose != 0 )
mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " );
for( secs = 1; secs <= 3; secs++ )
{
secs = 1;
(void) mbedtls_timing_get_timer( &hires, 1 );
mbedtls_set_alarm( (int) secs );
@ -421,12 +436,7 @@ int mbedtls_timing_self_test( int verbose )
/* For some reason on Windows it looks like alarm has an extra delay
* (maybe related to creating a new thread). Allow some room here. */
if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 )
{
if( verbose != 0 )
mbedtls_printf( "failed\n" );
return( 1 );
}
FAIL;
}
if( verbose != 0 )
@ -435,28 +445,22 @@ int mbedtls_timing_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( " TIMING test #2 (set/get_delay ): " );
for( a = 200; a <= 400; a += 200 )
{
for( b = 200; b <= 400; b += 200 )
{
mbedtls_timing_set_delay( &ctx, a, a + b );
a = 800;
b = 400;
mbedtls_timing_set_delay( &ctx, a, a + b ); /* T = 0 */
busy_msleep( a - a / 8 );
if( mbedtls_timing_get_delay( &ctx ) != 0 )
FAIL;
busy_msleep( a - a / 4 ); /* T = a - a/4 */
if( mbedtls_timing_get_delay( &ctx ) != 0 )
FAIL;
busy_msleep( a / 4 );
if( mbedtls_timing_get_delay( &ctx ) != 1 )
FAIL;
busy_msleep( a / 4 + b / 4 ); /* T = a + b/4 */
if( mbedtls_timing_get_delay( &ctx ) != 1 )
FAIL;
busy_msleep( b - a / 8 - b / 8 );
if( mbedtls_timing_get_delay( &ctx ) != 1 )
FAIL;
busy_msleep( b / 4 );
if( mbedtls_timing_get_delay( &ctx ) != 2 )
FAIL;
}
busy_msleep( b ); /* T = a + b + b/4 */
if( mbedtls_timing_get_delay( &ctx ) != 2 )
FAIL;
}
mbedtls_timing_set_delay( &ctx, 0, 0 );
@ -475,7 +479,6 @@ int mbedtls_timing_self_test( int verbose )
* On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
* since the whole test is about 10ms, it shouldn't happen twice in a row.
*/
hardfail = 0;
hard_test:
if( hardfail > 1 )

View file

@ -107,8 +107,8 @@ static int run_test_snprintf( void )
* self-test. If this fails, we attempt the test anyway, so no error is passed
* back.
*/
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_ENTROPY_C) && \
defined(MBEDTLS_ENTROPY_NV_SEED) && !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_ENTROPY_C)
#if defined(MBEDTLS_ENTROPY_NV_SEED) && !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
static void create_entropy_seed_file( void )
{
int result;
@ -136,9 +136,137 @@ static void create_entropy_seed_file( void )
}
#endif
int mbedtls_entropy_self_test_wrapper( int verbose )
{
#if defined(MBEDTLS_ENTROPY_NV_SEED) && !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
create_entropy_seed_file( );
#endif
return( mbedtls_entropy_self_test( verbose ) );
}
#endif
#if defined(MBEDTLS_SELF_TEST)
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
int mbedtls_memory_buffer_alloc_free_and_self_test( int verbose )
{
if( verbose != 0 )
{
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status( );
#endif
}
mbedtls_memory_buffer_alloc_free( );
return( mbedtls_memory_buffer_alloc_self_test( verbose ) );
}
#endif
typedef struct
{
const char *name;
int ( *function )( int );
} selftest_t;
const selftest_t selftests[] =
{
#if defined(MBEDTLS_MD2_C)
{"md2", mbedtls_md2_self_test},
#endif
#if defined(MBEDTLS_MD4_C)
{"md4", mbedtls_md4_self_test},
#endif
#if defined(MBEDTLS_MD5_C)
{"md5", mbedtls_md5_self_test},
#endif
#if defined(MBEDTLS_RIPEMD160_C)
{"ripemd160", mbedtls_ripemd160_self_test},
#endif
#if defined(MBEDTLS_SHA1_C)
{"sha1", mbedtls_sha1_self_test},
#endif
#if defined(MBEDTLS_SHA256_C)
{"sha256", mbedtls_sha256_self_test},
#endif
#if defined(MBEDTLS_SHA512_C)
{"sha512", mbedtls_sha512_self_test},
#endif
#if defined(MBEDTLS_ARC4_C)
{"arc4", mbedtls_arc4_self_test},
#endif
#if defined(MBEDTLS_DES_C)
{"des", mbedtls_des_self_test},
#endif
#if defined(MBEDTLS_AES_C)
{"aes", mbedtls_aes_self_test},
#endif
#if defined(MBEDTLS_GCM_C) && defined(MBEDTLS_AES_C)
{"gcm", mbedtls_gcm_self_test},
#endif
#if defined(MBEDTLS_CCM_C) && defined(MBEDTLS_AES_C)
{"ccm", mbedtls_ccm_self_test},
#endif
#if defined(MBEDTLS_CMAC_C)
{"cmac", mbedtls_cmac_self_test},
#endif
#if defined(MBEDTLS_BASE64_C)
{"base64", mbedtls_base64_self_test},
#endif
#if defined(MBEDTLS_BIGNUM_C)
{"mpi", mbedtls_mpi_self_test},
#endif
#if defined(MBEDTLS_RSA_C)
{"rsa", mbedtls_rsa_self_test},
#endif
#if defined(MBEDTLS_X509_USE_C)
{"x509", mbedtls_x509_self_test},
#endif
#if defined(MBEDTLS_XTEA_C)
{"xtea", mbedtls_xtea_self_test},
#endif
#if defined(MBEDTLS_CAMELLIA_C)
{"camellia", mbedtls_camellia_self_test},
#endif
#if defined(MBEDTLS_CTR_DRBG_C)
{"ctr_drbg", mbedtls_ctr_drbg_self_test},
#endif
#if defined(MBEDTLS_HMAC_DRBG_C)
{"hmac_drbg", mbedtls_hmac_drbg_self_test},
#endif
#if defined(MBEDTLS_ECP_C)
{"ecp", mbedtls_ecp_self_test},
#endif
#if defined(MBEDTLS_ECJPAKE_C)
{"ecjpake", mbedtls_ecjpake_self_test},
#endif
#if defined(MBEDTLS_DHM_C)
{"dhm", mbedtls_dhm_self_test},
#endif
#if defined(MBEDTLS_ENTROPY_C)
{"entropy", mbedtls_entropy_self_test_wrapper},
#endif
#if defined(MBEDTLS_PKCS5_C)
{"pkcs5", mbedtls_pkcs5_self_test},
#endif
/* Slower test after the faster ones */
#if defined(MBEDTLS_TIMING_C)
{"timing", mbedtls_timing_self_test},
#endif
/* Heap test comes last */
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
{"memory_buffer_alloc", mbedtls_memory_buffer_alloc_free_and_self_test},
#endif
{NULL, NULL}
};
#endif /* MBEDTLS_SELF_TEST */
int main( int argc, char *argv[] )
{
int v, suites_tested = 0, suites_failed = 0;
#if defined(MBEDTLS_SELF_TEST)
const selftest_t *test;
#endif /* MBEDTLS_SELF_TEST */
char **argp;
int v = 1; /* v=1 for verbose mode */
int exclude_mode = 0;
int suites_tested = 0, suites_failed = 0;
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && defined(MBEDTLS_SELF_TEST)
unsigned char buf[1000000];
#endif
@ -165,16 +293,24 @@ int main( int argc, char *argv[] )
mbedtls_exit( MBEDTLS_EXIT_FAILURE );
}
if( argc == 2 && ( strcmp( argv[1], "--quiet" ) == 0 ||
strcmp( argv[1], "-q" ) == 0 ) )
for( argp = argv + ( argc >= 1 ? 1 : argc ); *argp != NULL; ++argp )
{
v = 0;
if( strcmp( *argp, "--quiet" ) == 0 ||
strcmp( *argp, "-q" ) == 0 )
{
v = 0;
}
else if( strcmp( *argp, "--exclude" ) == 0 ||
strcmp( *argp, "-x" ) == 0 )
{
exclude_mode = 1;
}
else
break;
}
else
{
v = 1;
if( v != 0 )
mbedtls_printf( "\n" );
}
#if defined(MBEDTLS_SELF_TEST)
@ -182,246 +318,60 @@ int main( int argc, char *argv[] )
mbedtls_memory_buffer_alloc_init( buf, sizeof(buf) );
#endif
#if defined(MBEDTLS_MD2_C)
if( mbedtls_md2_self_test( v ) != 0 )
if( *argp != NULL && exclude_mode == 0 )
{
suites_failed++;
/* Run the specified tests */
for( ; *argp != NULL; argp++ )
{
for( test = selftests; test->name != NULL; test++ )
{
if( !strcmp( *argp, test->name ) )
{
if( test->function( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
break;
}
}
if( test->name == NULL )
{
mbedtls_printf( " Test suite %s not available -> failed\n\n", *argp );
suites_failed++;
}
}
}
suites_tested++;
#endif
#if defined(MBEDTLS_MD4_C)
if( mbedtls_md4_self_test( v ) != 0 )
else
{
suites_failed++;
/* Run all the tests except excluded ones */
for( test = selftests; test->name != NULL; test++ )
{
if( exclude_mode )
{
char **excluded;
for( excluded = argp; *excluded != NULL; ++excluded )
{
if( !strcmp( *excluded, test->name ) )
break;
}
if( *excluded )
{
if( v )
mbedtls_printf( " Skip: %s\n", test->name );
continue;
}
}
if( test->function( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
}
}
suites_tested++;
#endif
#if defined(MBEDTLS_MD5_C)
if( mbedtls_md5_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_RIPEMD160_C)
if( mbedtls_ripemd160_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_SHA1_C)
if( mbedtls_sha1_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_SHA256_C)
if( mbedtls_sha256_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_SHA512_C)
if( mbedtls_sha512_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_ARC4_C)
if( mbedtls_arc4_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_DES_C)
if( mbedtls_des_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_AES_C)
if( mbedtls_aes_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_GCM_C) && defined(MBEDTLS_AES_C)
if( mbedtls_gcm_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_CCM_C) && defined(MBEDTLS_AES_C)
if( mbedtls_ccm_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_CMAC_C)
if( ( mbedtls_cmac_self_test( v ) ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_BASE64_C)
if( mbedtls_base64_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_BIGNUM_C)
if( mbedtls_mpi_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_RSA_C)
if( mbedtls_rsa_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_X509_USE_C)
if( mbedtls_x509_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_XTEA_C)
if( mbedtls_xtea_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_CAMELLIA_C)
if( mbedtls_camellia_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_CTR_DRBG_C)
if( mbedtls_ctr_drbg_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_HMAC_DRBG_C)
if( mbedtls_hmac_drbg_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_ECP_C)
if( mbedtls_ecp_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_ECJPAKE_C)
if( mbedtls_ecjpake_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_DHM_C)
if( mbedtls_dhm_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_ENTROPY_C)
#if defined(MBEDTLS_ENTROPY_NV_SEED) && !defined(MBEDTLS_NO_PLATFORM_ENTROPY)
create_entropy_seed_file();
#endif
if( mbedtls_entropy_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#if defined(MBEDTLS_PKCS5_C)
if( mbedtls_pkcs5_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
/* Slow tests last */
#if defined(MBEDTLS_TIMING_C)
if( mbedtls_timing_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
if( v != 0 )
{
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status();
#endif
}
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
mbedtls_memory_buffer_alloc_free();
if( mbedtls_memory_buffer_alloc_self_test( v ) != 0 )
{
suites_failed++;
}
suites_tested++;
#endif
#else
(void) exclude_mode;
mbedtls_printf( " MBEDTLS_SELF_TEST not defined.\n" );
#endif

View file

@ -1,2 +1,41 @@
Timing selftest
timing_selftest:
Timing: basic timer operation
timing_timer_simple:
Timing: timer reset
timing_timer_reset:
Timing: two parallel timers, delay 0
timing_two_timers:0:
Timing: two parallel timers, delay 100
timing_two_timers:100:
Timing: two parallel timers, delay 1000
timing_two_timers:1000:
Timing: two parallel timers, delay 10000
timing_two_timers:10000:
Timing: delay 0ms, 0ms
timing_delay:0:0:
Timing: delay 0ms, 50ms
timing_delay:0:50:
Timing: delay 50ms, 50ms
timing_delay:50:50:
Timing: delay 50ms, 100ms
timing_delay:50:100:
Timing: delay 50ms, 200ms
timing_delay:50:200:
Timing: alarm in 0 second
timing_alarm:0:
Timing: alarm in 1 second
timing_alarm:1:
Timing: hardclock
timing_hardclock:

View file

@ -1,5 +1,51 @@
/* BEGIN_HEADER */
/* This test module exercises the timing module. One of the expected failure
modes is for timers to never expire, which could lead to an infinite loop.
The function timing_timer_simple is protected against this failure mode and
checks that timers do expire. Other functions will terminate if their
timers do expire. Therefore it is recommended to run timing_timer_simple
first and run other test functions only if that timing_timer_simple
succeeded. */
#include <limits.h>
#include "mbedtls/timing.h"
/* Wait this many milliseconds for a short timing test. This duration
should be large enough that, in practice, if you read the timer
value twice in a row, it won't have jumped by that much. */
#define TIMING_SHORT_TEST_MS 100
/* A loop that waits TIMING_SHORT_TEST_MS must not take more than this many
iterations. This value needs to be large enough to accommodate fast
platforms (e.g. at 4GHz and 10 cycles/iteration a CPU can run through 20
million iterations in 50ms). The only motivation to keep this value low is
to avoid having an infinite loop if the timer functions are not implemented
correctly. Ideally this value should be based on the processor speed but we
don't have this information! */
#define TIMING_SHORT_TEST_ITERATIONS_MAX 1e8
/* alarm(0) must fire in no longer than this amount of time. */
#define TIMING_ALARM_0_DELAY_MS TIMING_SHORT_TEST_MS
static int expected_delay_status( uint32_t int_ms, uint32_t fin_ms,
unsigned long actual_ms )
{
return( fin_ms == 0 ? -1 :
actual_ms >= fin_ms ? 2 :
actual_ms >= int_ms ? 1 :
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 */
/* BEGIN_DEPENDENCIES
@ -7,9 +53,351 @@
* END_DEPENDENCIES
*/
/* BEGIN_CASE depends_on:MBEDTLS_SELF_TEST */
void timing_selftest()
/* BEGIN_CASE */
void timing_timer_simple( )
{
TEST_ASSERT( mbedtls_timing_self_test( 1 ) == 0 );
struct mbedtls_timing_hr_time timer;
unsigned long millis = 0;
unsigned long new_millis = 0;
unsigned long iterations = 0;
/* Start the timer. */
(void) mbedtls_timing_get_timer( &timer, 1 );
/* Busy-wait loop for a few milliseconds. */
do
{
new_millis = mbedtls_timing_get_timer( &timer, 0 );
++iterations;
/* Check that the timer didn't go backwards */
TEST_ASSERT( new_millis >= millis );
millis = new_millis;
}
while( millis < TIMING_SHORT_TEST_MS &&
iterations <= TIMING_SHORT_TEST_ITERATIONS_MAX );
/* The wait duration should have been large enough for at least a
few runs through the loop, even on the slowest realistic platform. */
TEST_ASSERT( iterations >= 2 );
/* The wait duration shouldn't have overflowed the iteration count. */
TEST_ASSERT( iterations < TIMING_SHORT_TEST_ITERATIONS_MAX );
return;
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
problems can be hard to reproduce. */
mbedtls_fprintf( stdout, " Finished with millis=%lu new_millis=%lu get(timer)<=%lu iterations=%lu\n",
millis, new_millis, mbedtls_timing_get_timer( &timer, 0 ),
iterations );
}
/* END_CASE */
/* BEGIN_CASE */
void timing_timer_reset( )
{
struct mbedtls_timing_hr_time timer;
unsigned long millis = 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. */
TEST_ASSERT( mbedtls_timing_get_timer( &timer, 1 ) == 0 );
/* Busy-wait loop for a few milliseconds */
do
{
++iterations;
millis = mbedtls_timing_get_timer( &timer, 0 );
}
while( millis < TIMING_SHORT_TEST_MS );
/* Reset the timer and check that it has restarted. */
TEST_ASSERT( mbedtls_timing_get_timer( &timer, 1 ) == 0 );
/* Read the timer immediately after reset. It should be 0 or close
to it. */
TEST_ASSERT( mbedtls_timing_get_timer( &timer, 0 ) < TIMING_SHORT_TEST_MS );
return;
exit:
/* No cleanup needed, but show some diagnostic information, because timing
problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with millis=%lu get(timer)<=%lu iterations=%lu\n",
millis, mbedtls_timing_get_timer( &timer, 0 ),
iterations );
}
/* END_CASE */
/* BEGIN_CASE */
void timing_two_timers( int delta )
{
struct mbedtls_timing_hr_time timer1, timer2;
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. */
(void) mbedtls_timing_get_timer( &timer1, 1 );
do
{
millis1 = mbedtls_timing_get_timer( &timer1, 0 );
}
while( millis1 < TIMING_SHORT_TEST_MS );
/* Do a short busy-wait, so that the difference between timer1 and timer2
doesn't practically always end up being very close to a whole number of
milliseconds. */
while( delta > 0 )
--delta;
/* Start the second timer and compare it with the first. */
mbedtls_timing_get_timer( &timer2, 1 );
do
{
millis1 = mbedtls_timing_get_timer( &timer1, 0 );
millis2 = mbedtls_timing_get_timer( &timer2, 0 );
/* The first timer should always be ahead of the first. */
TEST_ASSERT( millis1 > millis2 );
/* The timers shouldn't drift apart, i.e. millis2-millis1 should stay
roughly constant, but this is hard to test reliably, especially in
a busy environment such as an overloaded continuous integration
system, so we don't test it it. */
}
while( millis2 < TIMING_SHORT_TEST_MS );
return;
exit:
/* No cleanup needed, but show some diagnostic iterations, because timing
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",
millis1, mbedtls_timing_get_timer( &timer1, 0 ),
millis2, mbedtls_timing_get_timer( &timer2, 0 ) );
}
/* END_CASE */
/* BEGIN_CASE */
void timing_alarm( int seconds )
{
struct mbedtls_timing_hr_time timer;
unsigned long millis = 0;
/* We check that about the desired number of seconds has elapsed. Be
slightly liberal with the lower bound, so as to allow platforms where
the alarm (with second resolution) and the timer (with millisecond
resolution) are based on different clocks. Be very liberal with the
upper bound, because the platform might be busy. */
unsigned long millis_min = ( seconds > 0 ?
seconds * 900 :
0 );
unsigned long millis_max = ( seconds > 0 ?
seconds * 1100 + 400 :
TIMING_ALARM_0_DELAY_MS );
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. */
(void) mbedtls_timing_get_timer( &timer, 1 );
mbedtls_set_alarm( seconds );
if( seconds > 0 )
{
/* We set the alarm for at least 1 second. It should not have fired
immediately, even on a slow and busy platform. */
TEST_ASSERT( !mbedtls_timing_alarmed );
}
/* A 0-second alarm should fire quickly, but we don't guarantee that it
fires immediately, so mbedtls_timing_alarmed may or may not be set at
this point. */
/* Busy-wait until the alarm rings */
do
{
++iterations;
millis = mbedtls_timing_get_timer( &timer, 0 );
}
while( !mbedtls_timing_alarmed && millis <= millis_max );
TEST_ASSERT( mbedtls_timing_alarmed );
TEST_ASSERT( millis >= millis_min );
TEST_ASSERT( millis <= millis_max );
mbedtls_timing_alarmed = 0;
return;
exit:
/* Show some diagnostic iterations, because timing
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_timing_alarmed,
millis, mbedtls_timing_get_timer( &timer, 0 ),
iterations );
/* Cleanup */
mbedtls_timing_alarmed = 0;
}
/* END_CASE */
/* BEGIN_CASE */
void timing_delay( int int_ms, int fin_ms )
{
/* This function assumes that if int_ms is nonzero then it is large
enough that we have time to read all timers at least once in an
interval of time lasting int_ms milliseconds, and likewise for (fin_ms
- int_ms). So don't call it with arguments that are too small. */
mbedtls_timing_delay_context delay;
struct mbedtls_timing_hr_time timer;
unsigned long delta = 0; /* delay started between timer=0 and timer=delta */
unsigned long before = 0, after = 0;
unsigned long iterations = 0;
int status = -2;
int saw_status_1 = 0;
int warn_inconclusive = 0;
assert( int_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
the delay is consistent with the time given by the reference timer. */
(void) mbedtls_timing_get_timer( &timer, 1 );
mbedtls_timing_set_delay( &delay, int_ms, fin_ms );
/* Set delta to an upper bound for the interval between the start of timer
and the start of delay. Reading timer after starting delay gives us an
upper bound for the interval, rounded to a 1ms precision. Since this
might have been rounded down, but we need an upper bound, we add 1. */
delta = mbedtls_timing_get_timer( &timer, 0 ) + 1;
status = mbedtls_timing_get_delay( &delay );
if( fin_ms == 0 )
{
/* Cancelled timer. Just check the correct status for this case. */
TEST_ASSERT( status == -1 );
return;
}
/* Initially, none of the delays must be passed yet if they're nonzero.
This could fail for very small values of int_ms and fin_ms, where "very
small" depends how fast and how busy the platform is. */
if( int_ms > 0 )
{
TEST_ASSERT( status == 0 );
}
else
{
TEST_ASSERT( status == 1 );
}
do
{
unsigned long delay_min, delay_max;
int status_min, status_max;
++iterations;
before = mbedtls_timing_get_timer( &timer, 0 );
status = mbedtls_timing_get_delay( &delay );
after = mbedtls_timing_get_timer( &timer, 0 );
/* At a time between before and after, the delay's status was status.
Check that this is consistent given that the delay was started
between times 0 and delta. */
delay_min = ( before > delta ? before - delta : 0 );
status_min = expected_delay_status( int_ms, fin_ms, delay_min );
delay_max = after;
status_max = expected_delay_status( int_ms, fin_ms, delay_max );
TEST_ASSERT( status >= status_min );
TEST_ASSERT( status <= status_max );
if( status == 1 )
saw_status_1 = 1;
}
while ( before <= fin_ms + delta && status != 2 );
/* Since we've waited at least fin_ms, the delay must have fully
expired. */
TEST_ASSERT( status == 2 );
/* If the second delay is more than the first, then there must have been a
point in time when the first delay was passed but not the second delay.
This could fail for very small values of (fin_ms - int_ms), where "very
small" depends how fast and how busy the platform is. In practice, this
is the test that's most likely to fail on a heavily loaded machine. */
if( fin_ms > int_ms )
{
warn_inconclusive = 1;
TEST_ASSERT( saw_status_1 );
}
return;
exit:
/* No cleanup needed, but show some diagnostic iterations, because timing
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",
delta, before, after, status, iterations );
if( warn_inconclusive )
mbedtls_fprintf( stdout, " Inconclusive test, try running it on a less heavily loaded machine.\n" );
}
/* END_CASE */
/* BEGIN_CASE */
void timing_hardclock( )
{
/* We make very few guarantees about mbedtls_timing_hardclock: its rate is
platform-dependent, it can wrap around. So there isn't much we can
test. But we do at least test that it doesn't crash, stall or return
completely nonsensical values. */
struct mbedtls_timing_hr_time timer;
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( );
/* Wait 2ms to ensure a nonzero delay. Since the timer interface has 1ms
resolution and unspecified precision, waiting 1ms might be a very small
delay that's rounded up. */
(void) mbedtls_timing_get_timer( &timer, 1 );
while( mbedtls_timing_get_timer( &timer, 0 ) < 2 )
/*busy-wait loop*/;
hardclock1 = mbedtls_timing_hardclock( );
/* Although the hardclock counter can wrap around, the difference
(hardclock1 - hardclock0) is taken modulo the type size, so it is
correct as long as the counter only wrapped around at most once. We
further require the difference to be nonzero (after a wait of more than
1ms, the counter must have changed), and not to be overly large (after
a wait of less than 3ms, plus time lost because other processes were
scheduled on the CPU). If the hardclock counter runs at 4GHz, then
1000000000 (which is 1/4 of the counter wraparound on a 32-bit machine)
allows 250ms. */
delta1 = hardclock1 - hardclock0;
TEST_ASSERT( delta1 > 0 );
TEST_ASSERT( delta1 < 1000000000 );
return;
exit:
/* No cleanup needed, but show some diagnostic iterations, because timing
problems can be hard to reproduce. */
if( !timers_are_badly_broken )
mbedtls_fprintf( stdout, " Finished with hardclock=%lu,%lu\n",
hardclock0, hardclock1 );
}
/* END_CASE */