/* It is a condition of use that safe_strings.cpp, safe_strings.h, safe_strings_test.cpp remain together. * * Maintained by portej05 - contact via PM on www.hard-light.net/forums * Why have we got this, what is it for? * VC2005+ define some safe string functions which check buffer sizes before doing anything * Unfortunately, GCC and MACOS do not provide these functions, therefore, we must! * (if only to reduce the amount of noise the static analysis tools are spitting out) * They are part of ISO/IEC TR 24731 and may find their way into the CRTs at some point, at which * point these functions must be removed from the engine. * While these functions do not add a huge amount of benefit for heap-allocated strings, they * can protect against a class of buffer overruns in stack allocated situations. * */ #ifdef SAFESTRINGS_TEST_APP #include #include #include "safe_strings.h" int last_errno = 0; void error_handler( int errnoValue, const char* errnoStr, const char* file, const char* function, int line ) { last_errno = errnoValue; (errnoStr); (file); (function); (line); } #define _RESET_ERRNO( ) last_errno = 0 #define _EXPECTED_ERRNO( errnoVal ) if ( last_errno != errnoVal ) printf("errno value of %d not found: %d %s(%d)\n",errnoVal,last_errno,__FILE__,__LINE__) #define _EXPECTED_LAST_ERRNO( errnoVal ) _EXPECTED_ERRNO( errnoVal ) #define _EXPECTED_RETURN( value, function ) if ( function != value ) printf("expected value not received: %s(%d)\n", __FILE__,__LINE__) #define _EXPECTED_STRING( str, value ) if ( !strcmp( str, value ) ) printf("strings do not match: %s(%d)\n", __FILE__, __LINE__) #define _EXPECTED_VALUE( val, value ) if ( val != value ) printf("values do not match: %s(%d)\n", __FILE__, __LINE__ ) /* Dumb memset (because we can't include string.h in this program) */ void dumb_memset( void* buf, char val, size_t bytes ) { char* p = (char*)buf; while ( bytes-- ) { *(p++) = val; } } bool strcmp( const char* str1, const char* str2 ) { while ( *str1 && *str2 ) { if ( *str1 != *str2 ) return false; str1++; str2++; } return ( !*str1 && !*str2 ); } void test_strcpy_s( ) { #define _RESET_STRINGS( ) dumb_memset( strSource, 0, 15 );\ dumb_memset( strDest, 0, 15 ); char strSource[ 15 ]; char strDest[ 15 ]; /* strcpy_s tests * Must test both strcpy_s functions (even though one calls the other) * 1) Copy string into larger buffer * - Expecting buffer to contain string + NULL * 2) Attempt to copy string into too small buffer * - strDest[0] = NULL * - calls __safe_strings_error_handler with ERANGE * - returns ERANGE * 3) strSource = NULL * - returns EINVAL * - calls __safe_strings_error_handler with EINVAL * - strDest[0] = NULL * 4) strDest = NULL * - calls __safe_strings_error_handler with EINVAL * - returns EINVAL * 5) strSource = NULL, strDest = NULL * - calls __safe_strings_error_handler with EINVAL * - returns EINVAL * 6) sizeInBytes = 0 * - calls __safe_strings_error_handler with ERANGE * - returns ERANGE * 7) A string with the size+NULL the same as the size of the buffer * - returns 0 * - strDest should contain the string */ _RESET_ERRNO( ); /* 1 */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello World" ) ); _EXPECTED_LAST_ERRNO( 0 ); _EXPECTED_STRING( strDest, "Hello World" ); _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello World" ) ); _EXPECTED_LAST_ERRNO( 0 ); _EXPECTED_STRING( strDest, "Hello World" ); /* 2 */ _RESET_STRINGS( ); _EXPECTED_RETURN( ERANGE, strcpy_s( strDest, "Hello World, this is a test" ) ); _EXPECTED_ERRNO( ERANGE ); _EXPECTED_VALUE( strDest[ 0 ], NULL ); _RESET_STRINGS( ); _EXPECTED_RETURN( ERANGE, strcpy_s( strDest, 15, "Hello World, this is a test" ) ); _EXPECTED_ERRNO( ERANGE ); _EXPECTED_VALUE( strDest[ 0 ], NULL ); /* 3 */ _RESET_STRINGS( ); _EXPECTED_RETURN( EINVAL, strcpy_s( strDest, NULL ) ); _EXPECTED_ERRNO( EINVAL ); _RESET_STRINGS( ); _EXPECTED_RETURN( EINVAL, strcpy_s( strDest, 15, NULL ) ); _EXPECTED_ERRNO( EINVAL ); /* 4 - can't be done on template version */ _RESET_STRINGS( ); _EXPECTED_RETURN( EINVAL, strcpy_s( NULL, 15, "Hello World" ) ); _EXPECTED_ERRNO( EINVAL ); /* 5 - can't be done on template version */ _RESET_STRINGS( ); _EXPECTED_RETURN( EINVAL, strcpy_s( NULL, 15, NULL ) ); _EXPECTED_ERRNO( EINVAL ); /* 6 - can't be done on strcpy_s template version */ _RESET_STRINGS( ); _EXPECTED_RETURN( ERANGE, strcpy_s( strDest, 0, "Hello World") ); _EXPECTED_ERRNO( ERANGE ); /* 7 */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello World th" ) ); _EXPECTED_LAST_ERRNO( ERANGE ); #undef _RESET_STRINGS } void test_strcat_s( ) { #define _RESET_STRINGS( ) dumb_memset( strSource, 0, 15 );\ dumb_memset( strDest, 0, 15 ); char strSource[ 15 ]; char strDest[ 15 ]; /* Test cases for strcat_s * 1) Normal concatenation where total string size < buffersize -1 * - No change in errno (i.e. handler is not called) * - returns 0 * 2) strSource = NULL, sizeInBytes != 0, strDest != NULL * - errno now EINVAL * - strDest[ 0 ] == NULL * - returns EINVAL * 3) strSource != NULL, sizeInBytes != 0, strDest == NULL * - errno now EINVAL * - returns EINVAL * 4) sizeInBytes = 0 * - errno now ERANGE * - returns ERANGE * 5) final string that is 15 chars+null * - returns 0 * - no change in errno */ _RESET_ERRNO( ); /* 1 */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello " ) ); _EXPECTED_LAST_ERRNO( 0 ); _EXPECTED_RETURN( 0, strcat_s( strDest, "World" ) ); _EXPECTED_LAST_ERRNO( 0 ); _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) ); _EXPECTED_LAST_ERRNO( 0 ); _EXPECTED_RETURN( 0, strcat_s( strDest, 15, "World" ) ); _EXPECTED_LAST_ERRNO( 0 ); /* 2 */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "World" ) ); _EXPECTED_LAST_ERRNO( 0); _EXPECTED_RETURN( EINVAL, strcat_s( strDest, NULL ) ); _EXPECTED_ERRNO( EINVAL ); _EXPECTED_VALUE( strDest[ 0 ], NULL ); _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "World" ) ); _EXPECTED_LAST_ERRNO( EINVAL ); _EXPECTED_RETURN( EINVAL, strcat_s( strDest, 15, NULL ) ); _EXPECTED_ERRNO( EINVAL ); _EXPECTED_VALUE( strDest[ 0 ], NULL ); /* 3 - can't be done with templated version */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strSource, "Hello " ) ); _EXPECTED_LAST_ERRNO( EINVAL ); _EXPECTED_RETURN( EINVAL, strcat_s( NULL, 15, strSource ) ); _EXPECTED_ERRNO( EINVAL ); /* 4 - can't be done with templated version*/ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) ); _EXPECTED_LAST_ERRNO( EINVAL ); _EXPECTED_RETURN( ERANGE, strcat_s( strDest, 0, "World" ) ); _EXPECTED_ERRNO( ERANGE ); /* 5 */ _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello " ) ); _EXPECTED_LAST_ERRNO( ERANGE ); _EXPECTED_RETURN( 0, strcpy_s( strDest, "World th" ) ); _EXPECTED_LAST_ERRNO( ERANGE ); _RESET_STRINGS( ); _EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) ); _EXPECTED_LAST_ERRNO( ERANGE ); _EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "World th" ) ); _EXPECTED_LAST_ERRNO( ERANGE ); #undef _RESET_STRINGS } int main(int argc, char* argv[]) { (argc); (argv); test_strcpy_s( ); test_strcat_s( ); printf("done.\n"); return 0; } #endif