1 /* It is a condition of use that safe_strings.cpp, safe_strings.h, safe_strings_test.cpp remain together.
2  *
3  * Maintained by portej05 - contact via PM on www.hard-light.net/forums
4  * Why have we got this, what is it for?
5  * VC2005+ define some safe string functions which check buffer sizes before doing anything
6  * Unfortunately, GCC and MACOS do not provide these functions, therefore, we must!
7  * (if only to reduce the amount of noise the static analysis tools are spitting out)
8  * They are part of ISO/IEC TR 24731 and may find their way into the CRTs at some point, at which
9  * point these functions must be removed from the engine.
10  * While these functions do not add a huge amount of benefit for heap-allocated strings, they
11  * can protect against a class of buffer overruns in stack allocated situations.
12  *
13  */
14 
15 #ifdef SAFESTRINGS_TEST_APP
16 
17 #include <stdio.h>
18 #include <errno.h>
19 #include "safe_strings.h"
20 
21 int last_errno = 0;
error_handler(int errnoValue,const char * errnoStr,const char * file,const char * function,int line)22 void error_handler( int errnoValue, const char* errnoStr, const char* file, const char* function, int line )
23 {
24 	last_errno = errnoValue;
25 	(errnoStr);
26 	(file);
27 	(function);
28 	(line);
29 }
30 
31 #define _RESET_ERRNO( ) last_errno = 0
32 
33 #define _EXPECTED_ERRNO( errnoVal ) if ( last_errno != errnoVal ) printf("errno value of %d not found: %d %s(%d)\n",errnoVal,last_errno,__FILE__,__LINE__)
34 #define _EXPECTED_LAST_ERRNO( errnoVal ) _EXPECTED_ERRNO( errnoVal )
35 #define _EXPECTED_RETURN( value, function ) if ( function != value ) printf("expected value not received: %s(%d)\n", __FILE__,__LINE__)
36 
37 #define _EXPECTED_STRING( str, value ) if ( !strcmp( str, value ) ) printf("strings do not match: %s(%d)\n", __FILE__, __LINE__)
38 #define _EXPECTED_VALUE( val, value ) if ( val != value ) printf("values do not match: %s(%d)\n", __FILE__, __LINE__ )
39 
40 /* Dumb memset (because we can't include string.h in this program) */
dumb_memset(void * buf,char val,size_t bytes)41 void dumb_memset( void* buf, char val, size_t bytes )
42 {
43 	char* p = (char*)buf;
44 
45 	while ( bytes-- )
46 	{
47 		*(p++) = val;
48 	}
49 }
50 
strcmp(const char * str1,const char * str2)51 bool strcmp( const char* str1, const char* str2 )
52 {
53 	while ( *str1 && *str2 )
54 	{
55 		if ( *str1 != *str2 )
56 			return false;
57 		str1++;
58 		str2++;
59 	}
60 
61 	return ( !*str1 && !*str2 );
62 }
63 
test_strcpy_s()64 void test_strcpy_s( )
65 {
66 #define _RESET_STRINGS( ) dumb_memset( strSource, 0, 15 );\
67 						  dumb_memset( strDest, 0, 15 );
68 
69 	char strSource[ 15 ];
70 	char strDest[ 15 ];
71 
72 	/* strcpy_s tests
73 	 * Must test both strcpy_s functions (even though one calls the other)
74 	 * 1) Copy string into larger buffer
75 	 *    - Expecting buffer to contain string + NULL
76 	 * 2) Attempt to copy string into too small buffer
77 	 *    - strDest[0] = NULL
78 	 *    - calls __safe_strings_error_handler with ERANGE
79 	 *    - returns ERANGE
80 	 * 3) strSource = NULL
81 	 *    - returns EINVAL
82 	 *    - calls __safe_strings_error_handler with EINVAL
83 	 *    - strDest[0] = NULL
84 	 * 4) strDest = NULL
85 	 *    - calls __safe_strings_error_handler with EINVAL
86 	 *    - returns EINVAL
87 	 * 5) strSource = NULL, strDest = NULL
88 	 *    - calls __safe_strings_error_handler with EINVAL
89 	 *	  - returns EINVAL
90 	 * 6) sizeInBytes = 0
91 	 *	  - calls __safe_strings_error_handler with ERANGE
92 	 *    - returns ERANGE
93 	 * 7) A string with the size+NULL the same as the size of the buffer
94 	 *    - returns 0
95 	 *    - strDest should contain the string
96 	 */
97 
98 	_RESET_ERRNO( );
99 
100 	/* 1 */
101 	_RESET_STRINGS( );
102 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello World" ) );
103 	_EXPECTED_LAST_ERRNO( 0 );
104 	_EXPECTED_STRING( strDest, "Hello World" );
105 
106 	_RESET_STRINGS( );
107 	_EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello World" ) );
108 	_EXPECTED_LAST_ERRNO( 0 );
109 	_EXPECTED_STRING( strDest, "Hello World" );
110 
111 
112 	/* 2 */
113 	_RESET_STRINGS( );
114 	_EXPECTED_RETURN( ERANGE, strcpy_s( strDest, "Hello World, this is a test" ) );
115 	_EXPECTED_ERRNO( ERANGE );
116 	_EXPECTED_VALUE( strDest[ 0 ], NULL );
117 
118 	_RESET_STRINGS( );
119 	_EXPECTED_RETURN( ERANGE, strcpy_s( strDest, 15, "Hello World, this is a test" ) );
120 	_EXPECTED_ERRNO( ERANGE );
121 	_EXPECTED_VALUE( strDest[ 0 ], NULL );
122 
123 	/* 3 */
124 	_RESET_STRINGS( );
125 	_EXPECTED_RETURN( EINVAL, strcpy_s( strDest, NULL ) );
126 	_EXPECTED_ERRNO( EINVAL );
127 
128 	_RESET_STRINGS( );
129 	_EXPECTED_RETURN( EINVAL, strcpy_s( strDest, 15, NULL ) );
130 	_EXPECTED_ERRNO( EINVAL );
131 
132 	/* 4 - can't be done on template version */
133 	_RESET_STRINGS( );
134 	_EXPECTED_RETURN( EINVAL, strcpy_s( NULL, 15, "Hello World" ) );
135 	_EXPECTED_ERRNO( EINVAL );
136 
137 	/* 5 - can't be done on template version */
138 	_RESET_STRINGS( );
139 	_EXPECTED_RETURN( EINVAL, strcpy_s( NULL, 15, NULL ) );
140 	_EXPECTED_ERRNO( EINVAL );
141 
142 	/* 6 - can't be done on strcpy_s template version */
143 	_RESET_STRINGS( );
144 	_EXPECTED_RETURN( ERANGE, strcpy_s( strDest, 0, "Hello World") );
145 	_EXPECTED_ERRNO( ERANGE );
146 
147 	/* 7 */
148 	_RESET_STRINGS( );
149 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello World th" ) );
150 	_EXPECTED_LAST_ERRNO( ERANGE );
151 
152 #undef _RESET_STRINGS
153 }
154 
test_strcat_s()155 void test_strcat_s( )
156 {
157 #define _RESET_STRINGS( ) dumb_memset( strSource, 0, 15 );\
158 						  dumb_memset( strDest, 0, 15 );
159 
160 	char strSource[ 15 ];
161 	char strDest[ 15 ];
162 
163 	/* Test cases for strcat_s
164 	 * 1) Normal concatenation where total string size < buffersize -1
165 	 *    - No change in errno (i.e. handler is not called)
166 	 *    - returns 0
167 	 * 2) strSource = NULL, sizeInBytes != 0, strDest != NULL
168 	 *    - errno now EINVAL
169 	 *    - strDest[ 0 ] == NULL
170 	 *    - returns EINVAL
171 	 * 3) strSource != NULL, sizeInBytes != 0, strDest == NULL
172 	 *    - errno now EINVAL
173 	 *    - returns EINVAL
174 	 * 4) sizeInBytes = 0
175 	 *    - errno now ERANGE
176 	 *    - returns ERANGE
177 	 * 5) final string that is 15 chars+null
178 	 *    - returns 0
179 	 *    - no change in errno
180 	 */
181 
182 	_RESET_ERRNO( );
183 
184 	/* 1 */
185 	_RESET_STRINGS( );
186 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello " ) );
187 	_EXPECTED_LAST_ERRNO( 0 );
188 	_EXPECTED_RETURN( 0, strcat_s( strDest, "World" ) );
189 	_EXPECTED_LAST_ERRNO( 0 );
190 
191 	_RESET_STRINGS( );
192 	_EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) );
193 	_EXPECTED_LAST_ERRNO( 0 );
194 	_EXPECTED_RETURN( 0, strcat_s( strDest, 15, "World" ) );
195 	_EXPECTED_LAST_ERRNO( 0 );
196 
197 	/* 2 */
198 	_RESET_STRINGS( );
199 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "World" ) );
200 	_EXPECTED_LAST_ERRNO(  0);
201 	_EXPECTED_RETURN( EINVAL, strcat_s( strDest, NULL ) );
202 	_EXPECTED_ERRNO( EINVAL );
203 	_EXPECTED_VALUE( strDest[ 0 ], NULL );
204 
205 	_RESET_STRINGS( );
206 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "World" ) );
207 	_EXPECTED_LAST_ERRNO( EINVAL );
208 	_EXPECTED_RETURN( EINVAL, strcat_s( strDest, 15, NULL ) );
209 	_EXPECTED_ERRNO( EINVAL );
210 	_EXPECTED_VALUE( strDest[ 0 ], NULL );
211 
212 	/* 3 - can't be done with templated version */
213 	_RESET_STRINGS( );
214 	_EXPECTED_RETURN( 0, strcpy_s( strSource, "Hello " ) );
215 	_EXPECTED_LAST_ERRNO( EINVAL );
216 	_EXPECTED_RETURN( EINVAL, strcat_s( NULL, 15, strSource ) );
217 	_EXPECTED_ERRNO( EINVAL );
218 
219 	/* 4 - can't be done with templated version*/
220 	_RESET_STRINGS( );
221 	_EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) );
222 	_EXPECTED_LAST_ERRNO( EINVAL );
223 	_EXPECTED_RETURN( ERANGE, strcat_s( strDest, 0, "World" ) );
224 	_EXPECTED_ERRNO( ERANGE );
225 
226 	/* 5 */
227 	_RESET_STRINGS( );
228 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "Hello " ) );
229 	_EXPECTED_LAST_ERRNO( ERANGE );
230 	_EXPECTED_RETURN( 0, strcpy_s( strDest, "World th" ) );
231 	_EXPECTED_LAST_ERRNO( ERANGE );
232 
233 	_RESET_STRINGS( );
234 	_EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "Hello " ) );
235 	_EXPECTED_LAST_ERRNO( ERANGE );
236 	_EXPECTED_RETURN( 0, strcpy_s( strDest, 15, "World th" ) );
237 	_EXPECTED_LAST_ERRNO( ERANGE );
238 
239 #undef _RESET_STRINGS
240 }
241 
main(int argc,char * argv[])242 int main(int argc, char* argv[])
243 {
244 	(argc);
245 	(argv);
246 
247 	test_strcpy_s( );
248 	test_strcat_s( );
249 
250 	printf("done.\n");
251 
252 	return 0;
253 }
254 
255 #endif
256