mbedtls/tests/suites/main_test.function
Gilles Peskine 0abb8e4bd8 Detect and report mutex usage errors
If the mutex usage verification framework is enabled and it detects a
mutex usage error, report this error and mark the test as failed.

This detects most usage errors, but not all cases of using
uninitialized memory (which is impossible in full generality) and not
leaks due to missing free (which will be handled in a subsequent commit).

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
2021-02-12 15:55:15 +01:00

555 lines
16 KiB
Plaintext

#line 2 "main_test.function"
SUITE_PRE_DEP
#define TEST_SUITE_ACTIVE
int verify_string( char **str )
{
if( (*str)[0] != '"' ||
(*str)[strlen( *str ) - 1] != '"' )
{
mbedtls_fprintf( stderr,
"Expected string (with \"\") for parameter and got: %s\n", *str );
return( -1 );
}
(*str)++;
(*str)[strlen( *str ) - 1] = '\0';
return( 0 );
}
int verify_int( char *str, int *value )
{
size_t i;
int minus = 0;
int digits = 1;
int hex = 0;
for( i = 0; i < strlen( str ); i++ )
{
if( i == 0 && str[i] == '-' )
{
minus = 1;
continue;
}
if( ( ( minus && i == 2 ) || ( !minus && i == 1 ) ) &&
str[i - 1] == '0' && str[i] == 'x' )
{
hex = 1;
continue;
}
if( ! ( ( str[i] >= '0' && str[i] <= '9' ) ||
( hex && ( ( str[i] >= 'a' && str[i] <= 'f' ) ||
( str[i] >= 'A' && str[i] <= 'F' ) ) ) ) )
{
digits = 0;
break;
}
}
if( digits )
{
if( hex )
*value = strtol( str, NULL, 16 );
else
*value = strtol( str, NULL, 10 );
return( 0 );
}
MAPPING_CODE
mbedtls_fprintf( stderr,
"Expected integer for parameter and got: %s\n", str );
return( KEY_VALUE_MAPPING_NOT_FOUND );
}
/*----------------------------------------------------------------------------*/
/* Test Case code */
FUNCTION_CODE
SUITE_POST_DEP
#line !LINE_NO! "main_test.function"
/*----------------------------------------------------------------------------*/
/* Test dispatch code */
int dep_check( char *str )
{
if( str == NULL )
return( 1 );
DEP_CHECK_CODE
#line !LINE_NO! "main_test.function"
return( DEPENDENCY_NOT_SUPPORTED );
}
int dispatch_test(int cnt, char *params[50])
{
int ret;
((void) cnt);
((void) params);
#if defined(TEST_SUITE_ACTIVE)
ret = DISPATCH_TEST_SUCCESS;
// Cast to void to avoid compiler warnings
(void)ret;
DISPATCH_FUNCTION
{
#line !LINE_NO! "main_test.function"
mbedtls_fprintf( stdout,
"FAILED\nSkipping unknown test function '%s'\n",
params[0] );
fflush( stdout );
ret = DISPATCH_TEST_FN_NOT_FOUND;
}
#else
ret = DISPATCH_UNSUPPORTED_SUITE;
#endif
return( ret );
}
/*----------------------------------------------------------------------------*/
/* Main Test code */
#line !LINE_NO! "main_test.function"
#define USAGE \
"Usage: %s [OPTIONS] files...\n\n" \
" Command line arguments:\n" \
" files... One or more test data file. If no file is specified\n" \
" the followimg default test case is used:\n" \
" %s\n\n" \
" Options:\n" \
" -v | --verbose Display full information about each test\n" \
" -h | --help Display this information\n\n", \
argv[0], \
"TESTCASE_FILENAME"
/** Retrieve one input line into buf, which must have room for len
* bytes. The trailing line break (if any) is stripped from the result.
* Lines beginning with the character '#' are skipped. Lines that are
* more than len-1 bytes long including the trailing line break are
* truncated; note that the following bytes remain in the input stream.
*
* \return 0 on success, -1 on error or end of file
*/
int get_line( FILE *f, char *buf, size_t len )
{
char *ret;
do
{
ret = fgets( buf, len, f );
if( ret == NULL )
return( -1 );
}
while( buf[0] == '#' );
ret = buf + strlen( buf );
if( ret-- > buf && *ret == '\n' )
*ret = '\0';
if( ret-- > buf && *ret == '\r' )
*ret = '\0';
return( 0 );
}
int parse_arguments( char *buf, size_t len, char *params[50] )
{
int cnt = 0, i;
char *cur = buf;
char *p = buf, *q;
params[cnt++] = cur;
while( *p != '\0' && p < buf + len )
{
if( *p == '\\' )
{
p++;
p++;
continue;
}
if( *p == ':' )
{
if( p + 1 < buf + len )
{
cur = p + 1;
params[cnt++] = cur;
}
*p = '\0';
}
p++;
}
/* Replace newlines, question marks and colons in strings */
for( i = 0; i < cnt; i++ )
{
p = params[i];
q = params[i];
while( *p != '\0' )
{
if( *p == '\\' && *(p + 1) == 'n' )
{
p += 2;
*(q++) = '\n';
}
else if( *p == '\\' && *(p + 1) == ':' )
{
p += 2;
*(q++) = ':';
}
else if( *p == '\\' && *(p + 1) == '?' )
{
p += 2;
*(q++) = '?';
}
else
*(q++) = *(p++);
}
*q = '\0';
}
return( cnt );
}
static int test_snprintf( size_t n, const char *ref_buf, int ref_ret )
{
int ret;
char buf[10] = "xxxxxxxxx";
const char ref[10] = "xxxxxxxxx";
ret = mbedtls_snprintf( buf, n, "%s", "123" );
if( ret < 0 || (size_t) ret >= n )
ret = -1;
if( strncmp( ref_buf, buf, sizeof( buf ) ) != 0 ||
ref_ret != ret ||
memcmp( buf + n, ref + n, sizeof( buf ) - n ) != 0 )
{
return( 1 );
}
return( 0 );
}
static int run_test_snprintf( void )
{
return( test_snprintf( 0, "xxxxxxxxx", -1 ) != 0 ||
test_snprintf( 1, "", -1 ) != 0 ||
test_snprintf( 2, "1", -1 ) != 0 ||
test_snprintf( 3, "12", -1 ) != 0 ||
test_snprintf( 4, "123", 3 ) != 0 ||
test_snprintf( 5, "123", 3 ) != 0 );
}
int main(int argc, const char *argv[])
{
/* Local Configurations and options */
const char *default_filename = "TESTCASE_FILENAME";
const char *test_filename = NULL;
const char **test_files = NULL;
size_t testfile_count = 0;
int option_verbose = 0;
/* Other Local variables */
int arg_index = 1;
const char *next_arg;
size_t testfile_index, i, cnt;
int ret;
unsigned total_errors = 0, total_tests = 0, total_skipped = 0;
FILE *file;
char buf[5000];
char *params[50];
void *pointer;
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
int stdout_fd = -1;
#endif /* __unix__ || __APPLE__ __MACH__ */
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
!defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
unsigned char alloc_buf[1000000];
mbedtls_memory_buffer_alloc_init( alloc_buf, sizeof(alloc_buf) );
#endif
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
mbedtls_test_mutex_usage_init( );
#endif
/*
* The C standard doesn't guarantee that all-bits-0 is the representation
* of a NULL pointer. We do however use that in our code for initializing
* structures, which should work on every modern platform. Let's be sure.
*/
memset( &pointer, 0, sizeof( void * ) );
if( pointer != NULL )
{
mbedtls_fprintf( stderr, "all-bits-zero is not a NULL pointer\n" );
return( 1 );
}
/*
* Make sure we have a snprintf that correctly zero-terminates
*/
if( run_test_snprintf() != 0 )
{
mbedtls_fprintf( stderr, "the snprintf implementation is broken\n" );
return( 0 );
}
while( arg_index < argc)
{
next_arg = argv[ arg_index ];
if( strcmp(next_arg, "--verbose" ) == 0 ||
strcmp(next_arg, "-v" ) == 0 )
{
option_verbose = 1;
}
else if( strcmp(next_arg, "--help" ) == 0 ||
strcmp(next_arg, "-h" ) == 0 )
{
mbedtls_fprintf( stdout, USAGE );
mbedtls_exit( EXIT_SUCCESS );
}
else
{
/* Not an option, therefore treat all further arguments as the file
* list.
*/
test_files = &argv[ arg_index ];
testfile_count = argc - arg_index;
}
arg_index++;
}
/* If no files were specified, assume a default */
if ( test_files == NULL || testfile_count == 0 )
{
test_files = &default_filename;
testfile_count = 1;
}
/* Initialize the struct that holds information about the last test */
memset( &test_info, 0, sizeof( test_info ) );
/* Now begin to execute the tests in the testfiles */
for ( testfile_index = 0;
testfile_index < testfile_count;
testfile_index++ )
{
size_t unmet_dep_count = 0;
char *unmet_dependencies[20]; /* only filled when verbose != 0 */
int missing_unmet_dependencies = 0;
test_filename = test_files[ testfile_index ];
file = fopen( test_filename, "r" );
if( file == NULL )
{
mbedtls_fprintf( stderr, "Failed to open test file: %s\n",
test_filename );
return( 1 );
}
while( !feof( file ) )
{
if( unmet_dep_count > 0 )
{
mbedtls_fprintf( stderr,
"FATAL: Dep count larger than zero at start of loop\n" );
mbedtls_exit( MBEDTLS_EXIT_FAILURE );
}
unmet_dep_count = 0;
memset( unmet_dependencies, 0, sizeof( unmet_dependencies ) );
missing_unmet_dependencies = 0;
if( ( ret = get_line( file, buf, sizeof(buf) ) ) != 0 )
break;
mbedtls_fprintf( stdout, "%s%.66s", test_info.failed ? "\n" : "", buf );
mbedtls_fprintf( stdout, " " );
for( i = strlen( buf ) + 1; i < 67; i++ )
mbedtls_fprintf( stdout, "." );
mbedtls_fprintf( stdout, " " );
fflush( stdout );
total_tests++;
if( ( ret = get_line( file, buf, sizeof(buf) ) ) != 0 )
break;
cnt = parse_arguments( buf, strlen(buf), params );
if( strcmp( params[0], "depends_on" ) == 0 )
{
for( i = 1; i < cnt; i++ )
{
if( dep_check( params[i] ) != DEPENDENCY_SUPPORTED )
{
if( unmet_dep_count <
ARRAY_LENGTH( unmet_dependencies ) )
{
if( 0 != option_verbose )
{
unmet_dependencies[unmet_dep_count] =
strdup( params[i] );
if( unmet_dependencies[unmet_dep_count] == NULL )
{
mbedtls_fprintf( stderr,
"FATAL: Out of memory\n" );
mbedtls_exit( MBEDTLS_EXIT_FAILURE );
}
}
unmet_dep_count++;
}
else
{
missing_unmet_dependencies = 1;
}
}
}
if( ( ret = get_line( file, buf, sizeof(buf) ) ) != 0 )
break;
cnt = parse_arguments( buf, strlen(buf), params );
}
// If there are no unmet dependencies execute the test
if( unmet_dep_count == 0 )
{
test_info.failed = 0;
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
/* Suppress all output from the library unless we're verbose
* mode
*/
if( !option_verbose )
{
stdout_fd = redirect_output( stdout, "/dev/null" );
if( stdout_fd == -1 )
{
/* Redirection has failed with no stdout so exit */
exit( 1 );
}
}
#endif /* __unix__ || __APPLE__ __MACH__ */
ret = dispatch_test( cnt, params );
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
if( !option_verbose && restore_output( stdout, stdout_fd ) )
{
/* Redirection has failed with no stdout so exit */
exit( 1 );
}
#endif /* __unix__ || __APPLE__ __MACH__ */
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
if( ret == DISPATCH_TEST_SUCCESS )
mbedtls_test_mutex_usage_check( );
#endif /* MBEDTLS_TEST_MUTEX_USAGE */
}
if( unmet_dep_count > 0 || ret == DISPATCH_UNSUPPORTED_SUITE )
{
total_skipped++;
mbedtls_fprintf( stdout, "----" );
if( 1 == option_verbose && ret == DISPATCH_UNSUPPORTED_SUITE )
{
mbedtls_fprintf( stdout, "\n Test Suite not enabled" );
}
if( 1 == option_verbose && unmet_dep_count > 0 )
{
mbedtls_fprintf( stdout, "\n Unmet dependencies: " );
for( i = 0; i < unmet_dep_count; i++ )
{
mbedtls_fprintf(stdout, "%s ",
unmet_dependencies[i]);
free(unmet_dependencies[i]);
}
if( missing_unmet_dependencies )
mbedtls_fprintf( stdout, "..." );
}
mbedtls_fprintf( stdout, "\n" );
fflush( stdout );
unmet_dep_count = 0;
missing_unmet_dependencies = 0;
}
else if( ret == DISPATCH_TEST_SUCCESS )
{
if( test_info.failed == 0 )
{
mbedtls_fprintf( stdout, "PASS\n" );
}
else
{
total_errors++;
mbedtls_fprintf( stdout, "FAILED\n" );
mbedtls_fprintf( stdout, " %s\n at line %d, %s\n",
test_info.test, test_info.line_no,
test_info.filename );
}
fflush( stdout );
}
else if( ret == DISPATCH_INVALID_TEST_DATA )
{
mbedtls_fprintf( stderr, "FAILED: FATAL PARSE ERROR\n" );
fclose( file );
mbedtls_exit( 2 );
}
else
total_errors++;
if( ( ret = get_line( file, buf, sizeof( buf ) ) ) != 0 )
break;
if( strlen( buf ) != 0 )
{
mbedtls_fprintf( stderr, "Should be empty %d\n",
(int) strlen( buf ) );
return( 1 );
}
}
fclose( file );
/* In case we encounter early end of file */
for( i = 0; i < unmet_dep_count; i++ )
free( unmet_dependencies[i] );
}
mbedtls_fprintf( stdout, "\n----------------------------------------------------------------------------\n\n");
if( total_errors == 0 )
mbedtls_fprintf( stdout, "PASSED" );
else
mbedtls_fprintf( stdout, "FAILED" );
mbedtls_fprintf( stdout, " (%u / %u tests (%u skipped))\n",
total_tests - total_errors, total_tests, total_skipped );
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C) && \
!defined(TEST_SUITE_MEMORY_BUFFER_ALLOC)
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_memory_buffer_alloc_status();
#endif
mbedtls_memory_buffer_alloc_free();
#endif
return( total_errors != 0 );
}