mirror of
https://github.com/yuzu-emu/mbedtls.git
synced 2025-06-13 09:45:36 +00:00
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>
690 lines
20 KiB
Plaintext
690 lines
20 KiB
Plaintext
#line 2 "helpers.function"
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Headers */
|
|
|
|
#include <stdlib.h>
|
|
|
|
#if defined(MBEDTLS_PLATFORM_C)
|
|
#include "mbedtls/platform.h"
|
|
#else
|
|
#include <stdio.h>
|
|
#define mbedtls_fprintf fprintf
|
|
#define mbedtls_snprintf snprintf
|
|
#define mbedtls_calloc calloc
|
|
#define mbedtls_free free
|
|
#define mbedtls_exit exit
|
|
#define mbedtls_time time
|
|
#define mbedtls_time_t time_t
|
|
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
|
|
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
|
|
#include "mbedtls/memory_buffer_alloc.h"
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#include <basetsd.h>
|
|
typedef UINT32 uint32_t;
|
|
#define strncasecmp _strnicmp
|
|
#define strcasecmp _stricmp
|
|
#else
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_THREADING_C) && defined(MBEDTLS_THREADING_PTHREAD) && \
|
|
defined(MBEDTLS_TEST_HOOKS)
|
|
#include "mbedtls/threading.h"
|
|
#define MBEDTLS_TEST_MUTEX_USAGE
|
|
#endif
|
|
|
|
/*
|
|
* Define the two macros
|
|
*
|
|
* #define TEST_CF_SECRET(ptr, size)
|
|
* #define TEST_CF_PUBLIC(ptr, size)
|
|
*
|
|
* that can be used in tests to mark a memory area as secret (no branch or
|
|
* memory access should depend on it) or public (default, only needs to be
|
|
* marked explicitly when it was derived from secret data).
|
|
*
|
|
* Arguments:
|
|
* - ptr: a pointer to the memory area to be marked
|
|
* - size: the size in bytes of the memory area
|
|
*
|
|
* Implementation:
|
|
* The basic idea is that of ctgrind <https://github.com/agl/ctgrind>: we can
|
|
* re-use tools that were designed for checking use of uninitialized memory.
|
|
* This file contains two implementations: one based on MemorySanitizer, the
|
|
* other on valgrind's memcheck. If none of them is enabled, dummy macros that
|
|
* do nothing are defined for convenience.
|
|
*/
|
|
#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
|
|
#include <sanitizer/msan_interface.h>
|
|
|
|
/* Use macros to avoid messing up with origin tracking */
|
|
#define TEST_CF_SECRET __msan_allocated_memory
|
|
// void __msan_allocated_memory(const volatile void* data, size_t size);
|
|
#define TEST_CF_PUBLIC __msan_unpoison
|
|
// void __msan_unpoison(const volatile void *a, size_t size);
|
|
|
|
#elif defined(MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND)
|
|
#include <valgrind/memcheck.h>
|
|
|
|
#define TEST_CF_SECRET VALGRIND_MAKE_MEM_UNDEFINED
|
|
// VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len)
|
|
#define TEST_CF_PUBLIC VALGRIND_MAKE_MEM_DEFINED
|
|
// VALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len)
|
|
|
|
#else /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN ||
|
|
MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND */
|
|
|
|
#define TEST_CF_SECRET(ptr, size)
|
|
#define TEST_CF_PUBLIC(ptr, size)
|
|
|
|
#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Constants */
|
|
|
|
#define DEPENDENCY_SUPPORTED 0
|
|
#define DEPENDENCY_NOT_SUPPORTED 1
|
|
|
|
#define KEY_VALUE_MAPPING_FOUND 0
|
|
#define KEY_VALUE_MAPPING_NOT_FOUND -1
|
|
|
|
#define DISPATCH_TEST_SUCCESS 0
|
|
#define DISPATCH_TEST_FN_NOT_FOUND 1
|
|
#define DISPATCH_INVALID_TEST_DATA 2
|
|
#define DISPATCH_UNSUPPORTED_SUITE 3
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Macros */
|
|
|
|
#define TEST_ASSERT( TEST ) \
|
|
do { \
|
|
if( ! (TEST) ) \
|
|
{ \
|
|
test_fail( #TEST, __LINE__, __FILE__ ); \
|
|
goto exit; \
|
|
} \
|
|
} while( 0 )
|
|
|
|
#define assert(a) if( !( a ) ) \
|
|
{ \
|
|
mbedtls_fprintf( stderr, "Assertion Failed at %s:%d - %s\n", \
|
|
__FILE__, __LINE__, #a ); \
|
|
mbedtls_exit( 1 ); \
|
|
}
|
|
|
|
#if defined(__GNUC__)
|
|
/* Test if arg and &(arg)[0] have the same type. This is true if arg is
|
|
* an array but not if it's a pointer. */
|
|
#define IS_ARRAY_NOT_POINTER( arg ) \
|
|
( ! __builtin_types_compatible_p( __typeof__( arg ), \
|
|
__typeof__( &( arg )[0] ) ) )
|
|
#else
|
|
/* On platforms where we don't know how to implement this check,
|
|
* omit it. Oh well, a non-portable check is better than nothing. */
|
|
#define IS_ARRAY_NOT_POINTER( arg ) 1
|
|
#endif
|
|
|
|
/* A compile-time constant with the value 0. If `const_expr` is not a
|
|
* compile-time constant with a nonzero value, cause a compile-time error. */
|
|
#define STATIC_ASSERT_EXPR( const_expr ) \
|
|
( 0 && sizeof( struct { unsigned int STATIC_ASSERT : 1 - 2 * ! ( const_expr ); } ) )
|
|
/* Return the scalar value `value` (possibly promoted). This is a compile-time
|
|
* constant if `value` is. `condition` must be a compile-time constant.
|
|
* If `condition` is false, arrange to cause a compile-time error. */
|
|
#define STATIC_ASSERT_THEN_RETURN( condition, value ) \
|
|
( STATIC_ASSERT_EXPR( condition ) ? 0 : ( value ) )
|
|
|
|
#define ARRAY_LENGTH_UNSAFE( array ) \
|
|
( sizeof( array ) / sizeof( *( array ) ) )
|
|
/** Return the number of elements of a static or stack array.
|
|
*
|
|
* \param array A value of array (not pointer) type.
|
|
*
|
|
* \return The number of elements of the array.
|
|
*/
|
|
#define ARRAY_LENGTH( array ) \
|
|
( STATIC_ASSERT_THEN_RETURN( IS_ARRAY_NOT_POINTER( array ), \
|
|
ARRAY_LENGTH_UNSAFE( array ) ) )
|
|
|
|
|
|
/*
|
|
* 32-bit integer manipulation macros (big endian)
|
|
*/
|
|
#ifndef GET_UINT32_BE
|
|
#define GET_UINT32_BE(n,b,i) \
|
|
{ \
|
|
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
|
|
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
|
|
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
|
|
| ( (uint32_t) (b)[(i) + 3] ); \
|
|
}
|
|
#endif
|
|
|
|
#ifndef PUT_UINT32_BE
|
|
#define PUT_UINT32_BE(n,b,i) \
|
|
{ \
|
|
(b)[(i) ] = (unsigned char) ( (n) >> 24 ); \
|
|
(b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \
|
|
(b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \
|
|
(b)[(i) + 3] = (unsigned char) ( (n) ); \
|
|
}
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Global variables */
|
|
|
|
|
|
static struct
|
|
{
|
|
int failed;
|
|
const char *test;
|
|
const char *filename;
|
|
int line_no;
|
|
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
|
|
const char *mutex_usage_error;
|
|
#endif
|
|
}
|
|
test_info;
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Helper flags for complex dependencies */
|
|
|
|
/* Indicates whether we expect mbedtls_entropy_init
|
|
* to initialize some strong entropy source. */
|
|
#if defined(MBEDTLS_TEST_NULL_ENTROPY) || \
|
|
( !defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) && \
|
|
( !defined(MBEDTLS_NO_PLATFORM_ENTROPY) || \
|
|
defined(MBEDTLS_HAVEGE_C) || \
|
|
defined(MBEDTLS_ENTROPY_HARDWARE_ALT) || \
|
|
defined(ENTROPY_NV_SEED) ) )
|
|
#define ENTROPY_HAVE_STRONG
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
/* Helper Functions */
|
|
|
|
void test_fail( const char *test, int line_no, const char* filename )
|
|
{
|
|
if( test_info.failed )
|
|
{
|
|
/* We've already recorded the test as having failed. Don't
|
|
* overwrite any previous information about the failure. */
|
|
return;
|
|
}
|
|
test_info.failed = 1;
|
|
test_info.test = test;
|
|
test_info.line_no = line_no;
|
|
test_info.filename = filename;
|
|
}
|
|
|
|
#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
|
|
static int redirect_output( FILE* out_stream, const char* path )
|
|
{
|
|
int out_fd, dup_fd;
|
|
FILE* path_stream;
|
|
|
|
out_fd = fileno( out_stream );
|
|
dup_fd = dup( out_fd );
|
|
|
|
if( dup_fd == -1 )
|
|
{
|
|
return( -1 );
|
|
}
|
|
|
|
path_stream = fopen( path, "w" );
|
|
if( path_stream == NULL )
|
|
{
|
|
close( dup_fd );
|
|
return( -1 );
|
|
}
|
|
|
|
fflush( out_stream );
|
|
if( dup2( fileno( path_stream ), out_fd ) == -1 )
|
|
{
|
|
close( dup_fd );
|
|
fclose( path_stream );
|
|
return( -1 );
|
|
}
|
|
|
|
fclose( path_stream );
|
|
return( dup_fd );
|
|
}
|
|
|
|
static int restore_output( FILE* out_stream, int dup_fd )
|
|
{
|
|
int out_fd = fileno( out_stream );
|
|
|
|
fflush( out_stream );
|
|
if( dup2( dup_fd, out_fd ) == -1 )
|
|
{
|
|
close( out_fd );
|
|
close( dup_fd );
|
|
return( -1 );
|
|
}
|
|
|
|
close( dup_fd );
|
|
return( 0 );
|
|
}
|
|
#endif /* __unix__ || __APPLE__ __MACH__ */
|
|
|
|
int unhexify( unsigned char *obuf, const char *ibuf )
|
|
{
|
|
unsigned char c, c2;
|
|
int len = strlen( ibuf ) / 2;
|
|
assert( strlen( ibuf ) % 2 == 0 ); /* must be even number of bytes */
|
|
|
|
while( *ibuf != 0 )
|
|
{
|
|
c = *ibuf++;
|
|
if( c >= '0' && c <= '9' )
|
|
c -= '0';
|
|
else if( c >= 'a' && c <= 'f' )
|
|
c -= 'a' - 10;
|
|
else if( c >= 'A' && c <= 'F' )
|
|
c -= 'A' - 10;
|
|
else
|
|
assert( 0 );
|
|
|
|
c2 = *ibuf++;
|
|
if( c2 >= '0' && c2 <= '9' )
|
|
c2 -= '0';
|
|
else if( c2 >= 'a' && c2 <= 'f' )
|
|
c2 -= 'a' - 10;
|
|
else if( c2 >= 'A' && c2 <= 'F' )
|
|
c2 -= 'A' - 10;
|
|
else
|
|
assert( 0 );
|
|
|
|
*obuf++ = ( c << 4 ) | c2;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
void hexify( unsigned char *obuf, const unsigned char *ibuf, int len )
|
|
{
|
|
unsigned char l, h;
|
|
|
|
while( len != 0 )
|
|
{
|
|
h = *ibuf / 16;
|
|
l = *ibuf % 16;
|
|
|
|
if( h < 10 )
|
|
*obuf++ = '0' + h;
|
|
else
|
|
*obuf++ = 'a' + h - 10;
|
|
|
|
if( l < 10 )
|
|
*obuf++ = '0' + l;
|
|
else
|
|
*obuf++ = 'a' + l - 10;
|
|
|
|
++ibuf;
|
|
len--;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocate and zeroize a buffer.
|
|
*
|
|
* If the size if zero, a pointer to a zeroized 1-byte buffer is returned.
|
|
*
|
|
* For convenience, dies if allocation fails.
|
|
*/
|
|
static unsigned char *zero_alloc( size_t len )
|
|
{
|
|
void *p;
|
|
size_t actual_len = ( len != 0 ) ? len : 1;
|
|
|
|
p = mbedtls_calloc( 1, actual_len );
|
|
assert( p != NULL );
|
|
|
|
memset( p, 0x00, actual_len );
|
|
|
|
return( p );
|
|
}
|
|
|
|
/**
|
|
* Allocate and fill a buffer from hex data.
|
|
*
|
|
* The buffer is sized exactly as needed. This allows to detect buffer
|
|
* overruns (including overreads) when running the test suite under valgrind.
|
|
*
|
|
* If the size if zero, a pointer to a zeroized 1-byte buffer is returned.
|
|
*
|
|
* For convenience, dies if allocation fails.
|
|
*/
|
|
unsigned char *unhexify_alloc( const char *ibuf, size_t *olen )
|
|
{
|
|
unsigned char *obuf;
|
|
|
|
*olen = strlen( ibuf ) / 2;
|
|
|
|
if( *olen == 0 )
|
|
return( zero_alloc( *olen ) );
|
|
|
|
obuf = mbedtls_calloc( 1, *olen );
|
|
assert( obuf != NULL );
|
|
|
|
(void) unhexify( obuf, ibuf );
|
|
|
|
return( obuf );
|
|
}
|
|
|
|
/**
|
|
* This function just returns data from rand().
|
|
* Although predictable and often similar on multiple
|
|
* runs, this does not result in identical random on
|
|
* each run. So do not use this if the results of a
|
|
* test depend on the random data that is generated.
|
|
*
|
|
* rng_state shall be NULL.
|
|
*/
|
|
static int rnd_std_rand( void *rng_state, unsigned char *output, size_t len )
|
|
{
|
|
#if !defined(__OpenBSD__) && !defined(__NetBSD__)
|
|
size_t i;
|
|
|
|
if( rng_state != NULL )
|
|
rng_state = NULL;
|
|
|
|
for( i = 0; i < len; ++i )
|
|
output[i] = rand();
|
|
#else
|
|
if( rng_state != NULL )
|
|
rng_state = NULL;
|
|
|
|
arc4random_buf( output, len );
|
|
#endif /* !OpenBSD && !NetBSD */
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/**
|
|
* This function only returns zeros
|
|
*
|
|
* rng_state shall be NULL.
|
|
*/
|
|
int rnd_zero_rand( void *rng_state, unsigned char *output, size_t len )
|
|
{
|
|
if( rng_state != NULL )
|
|
rng_state = NULL;
|
|
|
|
memset( output, 0, len );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
unsigned char *buf;
|
|
size_t length;
|
|
} rnd_buf_info;
|
|
|
|
/**
|
|
* This function returns random based on a buffer it receives.
|
|
*
|
|
* rng_state shall be a pointer to a rnd_buf_info structure.
|
|
*
|
|
* The number of bytes released from the buffer on each call to
|
|
* the random function is specified by per_call. (Can be between
|
|
* 1 and 4)
|
|
*
|
|
* After the buffer is empty it will return rand();
|
|
*/
|
|
int rnd_buffer_rand( void *rng_state, unsigned char *output, size_t len )
|
|
{
|
|
rnd_buf_info *info = (rnd_buf_info *) rng_state;
|
|
size_t use_len;
|
|
|
|
if( rng_state == NULL )
|
|
return( rnd_std_rand( NULL, output, len ) );
|
|
|
|
use_len = len;
|
|
if( len > info->length )
|
|
use_len = info->length;
|
|
|
|
if( use_len )
|
|
{
|
|
memcpy( output, info->buf, use_len );
|
|
info->buf += use_len;
|
|
info->length -= use_len;
|
|
}
|
|
|
|
if( len - use_len > 0 )
|
|
return( rnd_std_rand( NULL, output + use_len, len - use_len ) );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/**
|
|
* Info structure for the pseudo random function
|
|
*
|
|
* Key should be set at the start to a test-unique value.
|
|
* Do not forget endianness!
|
|
* State( v0, v1 ) should be set to zero.
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint32_t key[16];
|
|
uint32_t v0, v1;
|
|
} rnd_pseudo_info;
|
|
|
|
/**
|
|
* This function returns random based on a pseudo random function.
|
|
* This means the results should be identical on all systems.
|
|
* Pseudo random is based on the XTEA encryption algorithm to
|
|
* generate pseudorandom.
|
|
*
|
|
* rng_state shall be a pointer to a rnd_pseudo_info structure.
|
|
*/
|
|
int rnd_pseudo_rand( void *rng_state, unsigned char *output, size_t len )
|
|
{
|
|
rnd_pseudo_info *info = (rnd_pseudo_info *) rng_state;
|
|
uint32_t i, *k, sum, delta=0x9E3779B9;
|
|
unsigned char result[4], *out = output;
|
|
|
|
if( rng_state == NULL )
|
|
return( rnd_std_rand( NULL, output, len ) );
|
|
|
|
k = info->key;
|
|
|
|
while( len > 0 )
|
|
{
|
|
size_t use_len = ( len > 4 ) ? 4 : len;
|
|
sum = 0;
|
|
|
|
for( i = 0; i < 32; i++ )
|
|
{
|
|
info->v0 += ( ( ( info->v1 << 4 ) ^ ( info->v1 >> 5 ) )
|
|
+ info->v1 ) ^ ( sum + k[sum & 3] );
|
|
sum += delta;
|
|
info->v1 += ( ( ( info->v0 << 4 ) ^ ( info->v0 >> 5 ) )
|
|
+ info->v0 ) ^ ( sum + k[( sum>>11 ) & 3] );
|
|
}
|
|
|
|
PUT_UINT32_BE( info->v0, result, 0 );
|
|
memcpy( out, result, use_len );
|
|
len -= use_len;
|
|
out += 4;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_TEST_MUTEX_USAGE)
|
|
/** Mutex usage verification framework.
|
|
*
|
|
* The mutex usage verification code below aims to detect bad usage of
|
|
* Mbed TLS's mutex abstraction layer at runtime. Note that this is solely
|
|
* about the use of the mutex itself, not about checking whether the mutex
|
|
* correctly protects whatever it is supposed to protect.
|
|
*
|
|
* The normal usage of a mutex is:
|
|
* ```
|
|
* digraph mutex_states {
|
|
* "UNINITIALIZED"; // the initial state
|
|
* "IDLE";
|
|
* "FREED";
|
|
* "LOCKED";
|
|
* "UNINITIALIZED" -> "IDLE" [label="init"];
|
|
* "FREED" -> "IDLE" [label="init"];
|
|
* "IDLE" -> "LOCKED" [label="lock"];
|
|
* "LOCKED" -> "IDLE" [label="unlock"];
|
|
* "IDLE" -> "FREED" [label="free"];
|
|
* }
|
|
* ```
|
|
*
|
|
* All bad transitions that can be unambiguously detected are reported.
|
|
* An attempt to use an uninitialized mutex cannot be detected in general
|
|
* since the memory content may happen to denote a valid state. For the same
|
|
* reason, a double init cannot be detected.
|
|
* All-bits-zero is the state of a freed mutex, which is distinct from an
|
|
* initialized mutex, so attempting to use zero-initialized memory as a mutex
|
|
* without calling the init function is detected.
|
|
*
|
|
* If an error is detected, this framework will report what happened and the
|
|
* test case will be marked as failed. Unfortunately, the error report cannot
|
|
* indicate the exact location of the problematic call. To locate the error,
|
|
* use a debugger and set a breakpoint on mbedtls_test_mutex_usage_error().
|
|
*/
|
|
enum value_of_mutex_is_valid
|
|
{
|
|
MUTEX_FREED = 0, //!< Set by threading_mutex_free_pthread
|
|
MUTEX_IDLE = 1, //!< Set by threading_mutex_init_pthread and by our unlock
|
|
MUTEX_LOCKED = 2, //!< Set by our lock
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
void (*init)( mbedtls_threading_mutex_t * );
|
|
void (*free)( mbedtls_threading_mutex_t * );
|
|
int (*lock)( mbedtls_threading_mutex_t * );
|
|
int (*unlock)( mbedtls_threading_mutex_t * );
|
|
} mutex_functions_t;
|
|
static mutex_functions_t mutex_functions;
|
|
|
|
static void mbedtls_test_mutex_usage_error( mbedtls_threading_mutex_t *mutex,
|
|
const char *msg )
|
|
{
|
|
(void) mutex;
|
|
if( test_info.mutex_usage_error == NULL )
|
|
test_info.mutex_usage_error = msg;
|
|
mbedtls_fprintf( stdout, "[mutex: %s] ", msg );
|
|
/* Don't mark the test as failed yet. This way, if the test fails later
|
|
* for a functional reason, the test framework will report the message
|
|
* and location for this functional reason. If the test passes,
|
|
* mbedtls_test_mutex_usage_check() will mark it as failed. */
|
|
}
|
|
|
|
static void mbedtls_test_wrap_mutex_init( mbedtls_threading_mutex_t *mutex )
|
|
{
|
|
mutex_functions.init( mutex );
|
|
}
|
|
|
|
static void mbedtls_test_wrap_mutex_free( mbedtls_threading_mutex_t *mutex )
|
|
{
|
|
switch( mutex->is_valid )
|
|
{
|
|
case MUTEX_FREED:
|
|
mbedtls_test_mutex_usage_error( mutex, "free without init or double free" );
|
|
break;
|
|
case MUTEX_IDLE:
|
|
/* Do nothing. The underlying free function will reset is_valid
|
|
* to 0. */
|
|
break;
|
|
case MUTEX_LOCKED:
|
|
mbedtls_test_mutex_usage_error( mutex, "free without unlock" );
|
|
break;
|
|
default:
|
|
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
|
|
break;
|
|
}
|
|
mutex_functions.free( mutex );
|
|
}
|
|
|
|
static int mbedtls_test_wrap_mutex_lock( mbedtls_threading_mutex_t *mutex )
|
|
{
|
|
int ret = mutex_functions.lock( mutex );
|
|
switch( mutex->is_valid )
|
|
{
|
|
case MUTEX_FREED:
|
|
mbedtls_test_mutex_usage_error( mutex, "lock without init" );
|
|
break;
|
|
case MUTEX_IDLE:
|
|
if( ret == 0 )
|
|
mutex->is_valid = 2;
|
|
break;
|
|
case MUTEX_LOCKED:
|
|
mbedtls_test_mutex_usage_error( mutex, "double lock" );
|
|
break;
|
|
default:
|
|
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
|
|
break;
|
|
}
|
|
return( ret );
|
|
}
|
|
|
|
static int mbedtls_test_wrap_mutex_unlock( mbedtls_threading_mutex_t *mutex )
|
|
{
|
|
int ret = mutex_functions.unlock( mutex );
|
|
switch( mutex->is_valid )
|
|
{
|
|
case MUTEX_FREED:
|
|
mbedtls_test_mutex_usage_error( mutex, "unlock without init" );
|
|
break;
|
|
case MUTEX_IDLE:
|
|
mbedtls_test_mutex_usage_error( mutex, "unlock without lock" );
|
|
break;
|
|
case MUTEX_LOCKED:
|
|
if( ret == 0 )
|
|
mutex->is_valid = MUTEX_IDLE;
|
|
break;
|
|
default:
|
|
mbedtls_test_mutex_usage_error( mutex, "corrupted state" );
|
|
break;
|
|
}
|
|
return( ret );
|
|
}
|
|
|
|
static void mbedtls_test_mutex_usage_init( void )
|
|
{
|
|
mutex_functions.init = mbedtls_mutex_init;
|
|
mutex_functions.free = mbedtls_mutex_free;
|
|
mutex_functions.lock = mbedtls_mutex_lock;
|
|
mutex_functions.unlock = mbedtls_mutex_unlock;
|
|
mbedtls_mutex_init = &mbedtls_test_wrap_mutex_init;
|
|
mbedtls_mutex_free = &mbedtls_test_wrap_mutex_free;
|
|
mbedtls_mutex_lock = &mbedtls_test_wrap_mutex_lock;
|
|
mbedtls_mutex_unlock = &mbedtls_test_wrap_mutex_unlock;
|
|
}
|
|
|
|
static void mbedtls_test_mutex_usage_check( void )
|
|
{
|
|
if( test_info.mutex_usage_error != NULL && ! test_info.failed )
|
|
{
|
|
/* Functionally, the test passed. But there was a mutex usage error,
|
|
* so mark the test as failed after all. */
|
|
test_fail( "Mutex usage error", __LINE__, __FILE__ );
|
|
}
|
|
test_info.mutex_usage_error = NULL;
|
|
}
|
|
|
|
#endif /* MBEDTLS_TEST_MUTEX_USAGE */
|