1 /****************************************************************************
2 * *
3 * cryptlib Internal Memory Management API *
4 * Copyright Peter Gutmann 1992-2007 *
5 * *
6 ****************************************************************************/
7
8 #include <stdarg.h>
9 #if defined( INC_ALL )
10 #include "crypt.h"
11 #else
12 #include "crypt.h"
13 #endif /* Compiler-specific includes */
14
15 /****************************************************************************
16 * *
17 * Dynamic Buffer Management Routines *
18 * *
19 ****************************************************************************/
20
21 /* Dynamic buffer management functions. When reading variable-length
22 object data we can usually fit the data into a small fixed-length buffer
23 but occasionally we have to cope with larger data amounts that require a
24 dynamically-allocated buffer. The following routines manage this
25 process, dynamically allocating and freeing a larger buffer if required */
26
27 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
getDynData(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_MESSAGE const MESSAGE_TYPE message,IN_INT const int messageParam)28 static int getDynData( OUT DYNBUF *dynBuf,
29 IN_HANDLE const CRYPT_HANDLE cryptHandle,
30 IN_MESSAGE const MESSAGE_TYPE message,
31 IN_INT const int messageParam )
32 {
33 MESSAGE_DATA msgData;
34 void *dataPtr = NULL;
35 int status;
36
37 assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
38
39 REQUIRES( isHandleRangeValid( cryptHandle ) );
40 REQUIRES( ( message == IMESSAGE_GETATTRIBUTE_S && \
41 ( isAttribute( messageParam ) || \
42 isInternalAttribute( messageParam ) ) ) || \
43 ( message == IMESSAGE_CRT_EXPORT && \
44 ( messageParam == CRYPT_CERTFORMAT_CERTIFICATE || \
45 messageParam == CRYPT_CERTFORMAT_CERTCHAIN ) ) );
46
47 /* Clear return values. Note that we don't use the usual memset() to
48 clear the value since the structure contains the storage for the
49 fixed-size portion of the buffer appended to it, and using memset()
50 to clear that is just unnecessary overhead */
51 dynBuf->data = dynBuf->dataBuffer;
52 dynBuf->length = 0;
53
54 /* Get the data from the object */
55 setMessageData( &msgData, NULL, 0 );
56 status = krnlSendMessage( cryptHandle, message, &msgData, messageParam );
57 if( cryptStatusError( status ) )
58 return( status );
59 if( msgData.length > DYNBUF_SIZE )
60 {
61 /* The data is larger than the built-in buffer size, dynamically
62 allocate a larger buffer */
63 if( ( dataPtr = clDynAlloc( "getDynData", msgData.length ) ) == NULL )
64 return( CRYPT_ERROR_MEMORY );
65 msgData.data = dataPtr;
66 status = krnlSendMessage( cryptHandle, message, &msgData,
67 messageParam );
68 if( cryptStatusError( status ) )
69 {
70 clFree( "getDynData", dataPtr );
71 return( status );
72 }
73 dynBuf->data = dataPtr;
74 }
75 else
76 {
77 /* The data will fit into the built-in buffer, read it directly into
78 the buffer */
79 msgData.data = dynBuf->data;
80 status = krnlSendMessage( cryptHandle, message, &msgData,
81 messageParam );
82 if( cryptStatusError( status ) )
83 return( status );
84 }
85 dynBuf->length = msgData.length;
86
87 return( CRYPT_OK );
88 }
89
90 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
dynCreate(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeType)91 int dynCreate( OUT DYNBUF *dynBuf,
92 IN_HANDLE const CRYPT_HANDLE cryptHandle,
93 IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE attributeType )
94 {
95 assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
96
97 REQUIRES( isHandleRangeValid( cryptHandle ) );
98 REQUIRES( isAttribute( attributeType ) || \
99 isInternalAttribute( attributeType ) );
100
101 return( getDynData( dynBuf, cryptHandle, IMESSAGE_GETATTRIBUTE_S,
102 attributeType ) );
103 }
104
105 CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
dynCreateCert(OUT DYNBUF * dynBuf,IN_HANDLE const CRYPT_HANDLE cryptHandle,IN_ENUM (CRYPT_CERTFORMAT)const CRYPT_CERTFORMAT_TYPE formatType)106 int dynCreateCert( OUT DYNBUF *dynBuf,
107 IN_HANDLE const CRYPT_HANDLE cryptHandle,
108 IN_ENUM( CRYPT_CERTFORMAT ) \
109 const CRYPT_CERTFORMAT_TYPE formatType )
110 {
111 assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
112
113 REQUIRES( isHandleRangeValid( cryptHandle ) );
114 REQUIRES( formatType == CRYPT_CERTFORMAT_CERTIFICATE || \
115 formatType == CRYPT_CERTFORMAT_CERTCHAIN );
116
117 return( getDynData( dynBuf, cryptHandle, IMESSAGE_CRT_EXPORT,
118 formatType ) );
119 }
120
121 STDC_NONNULL_ARG( ( 1 ) ) \
dynDestroy(INOUT DYNBUF * dynBuf)122 void dynDestroy( INOUT DYNBUF *dynBuf )
123 {
124 assert( isWritePtr( dynBuf, sizeof( DYNBUF ) ) );
125 assert( isWritePtr( dynBuf->data, dynBuf->length ) );
126
127 REQUIRES_V( dynBuf->data != NULL );
128 REQUIRES_V( dynBuf->length > 0 && dynBuf->length < MAX_BUFFER_SIZE );
129
130 zeroise( dynBuf->data, dynBuf->length );
131 if( dynBuf->data != dynBuf->dataBuffer )
132 clFree( "dynDestroy", dynBuf->data );
133 dynBuf->data = NULL;
134 dynBuf->length = 0;
135 }
136
137 /****************************************************************************
138 * *
139 * Memory Pool Management Routines *
140 * *
141 ****************************************************************************/
142
143 /* Memory pool management functions. When allocating many small blocks of
144 memory, especially in resource-constrained systems, it's better if we pre-
145 allocate a small memory pool ourselves and grab chunks of it as required,
146 falling back to dynamically allocating memory later on if we exhaust the
147 pool. The following functions implement the custom memory pool
148 management. Usage is:
149
150 initMemPool( &memPoolState, storage, storageSize );
151 newItem = getMemPool( &memPoolState, newItemSize ) */
152
153 typedef struct {
154 BUFFER( storageSize, storagePos )
155 void *storage; /* Memory pool */
156 int storageSize, storagePos; /* Current usage and total size of pool */
157 } MEMPOOL_INFO;
158
159 CHECK_RETVAL_BOOL STDC_NONNULL_ARG( ( 1 ) ) \
sanityCheck(const MEMPOOL_INFO * state)160 static BOOLEAN sanityCheck( const MEMPOOL_INFO *state )
161 {
162 /* Make sure that the overall pool size information is in order */
163 if( state->storageSize < 64 || \
164 state->storageSize >= MAX_INTLENGTH_SHORT )
165 return( FALSE );
166
167 /* Make sure that the pool allocation information is in order */
168 if( state->storagePos < 0 || \
169 state->storagePos >= MAX_INTLENGTH_SHORT || \
170 state->storagePos > state->storageSize )
171 return( FALSE );
172
173 return( TRUE );
174 }
175
176 CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
177 int initMemPool( OUT void *statePtr,
178 IN_BUFFER( memPoolSize ) void *memPool,
179 IN_LENGTH_SHORT_MIN( 64 ) const int memPoolSize )
180 {
181 MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
182
183 assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
184 assert( isWritePtr( memPool, memPoolSize ) );
185
186 #if defined( __WIN32__ ) && defined( _MSC_VER )
187 #pragma warning( disable: 4127 ) /* Needed for sizeof() in check */
188 #endif /* VC++ */
189 REQUIRES( sizeof( MEMPOOL_STATE ) >= sizeof( MEMPOOL_INFO ) );
190 REQUIRES( memPoolSize >= 64 && memPoolSize < MAX_INTLENGTH_SHORT );
191 #if defined( __WIN32__ ) && defined( _MSC_VER )
192 #pragma warning( 4: 4127 )
193 #endif /* VC++ */
194
195 memset( state, 0, sizeof( MEMPOOL_INFO ) );
196 state->storage = memPool;
197 state->storageSize = memPoolSize;
198
199 return( CRYPT_OK );
200 }
201
202 CHECK_RETVAL_PTR STDC_NONNULL_ARG( ( 1 ) ) \
getMemPool(INOUT void * statePtr,IN_LENGTH_SHORT const int size)203 void *getMemPool( INOUT void *statePtr, IN_LENGTH_SHORT const int size )
204 {
205 MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
206 BYTE *allocPtr;
207 const int allocSize = roundUp( size, sizeof( int ) );
208
209 assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
210 assert( isWritePtr( state->storage, state->storageSize ) );
211
212 REQUIRES_N( size > 0 && size < MAX_INTLENGTH_SHORT );
213 REQUIRES_N( allocSize >= sizeof( int ) && \
214 allocSize < MAX_INTLENGTH_SHORT );
215 REQUIRES_N( sanityCheck( state ) );
216
217 /* If we can't satisfy the request from the memory pool we have to
218 allocate the memory block dynamically */
219 if( state->storagePos + allocSize > state->storageSize )
220 return( clDynAlloc( "getMemPool", size ) );
221
222 /* We can satisfy the request from the pool:
223
224 memPool
225 |
226 v <- size -->
227 +-------+-----------+-------+
228 | | | |
229 +-------+-----------+-------+
230 ^ ^
231 | |
232 storagePos storagePos' */
233 allocPtr = ( BYTE * ) state->storage + state->storagePos;
234 state->storagePos += allocSize;
235 ENSURES_N( sanityCheck( state ) );
236
237 return( allocPtr );
238 }
239
240 STDC_NONNULL_ARG( ( 1, 2 ) ) \
freeMemPool(INOUT void * statePtr,IN void * memblock)241 void freeMemPool( INOUT void *statePtr, IN void *memblock )
242 {
243 MEMPOOL_INFO *state = ( MEMPOOL_INFO * ) statePtr;
244
245 assert( isWritePtr( state, sizeof( MEMPOOL_INFO ) ) );
246 assert( isWritePtr( state->storage, state->storageSize ) );
247
248 REQUIRES_V( sanityCheck( state ) );
249
250 /* If the memory block to free lies within the pool, there's nothing to
251 do */
252 if( memblock >= state->storage && \
253 memblock < ( void * ) ( ( BYTE * ) state->storage + \
254 state->storageSize ) )
255 return;
256
257 /* It's outside the pool and therefore dynamically allocated, free it */
258 clFree( "freeMemPool", memblock );
259 }
260
261 /****************************************************************************
262 * *
263 * Debugging Malloc Support *
264 * *
265 ****************************************************************************/
266
267 /* Debugging malloc() that dumps memory usage diagnostics to stdout. Note
268 that these functions are only intended to be used during interactive
269 debugging sessions since they throw exceptions under error conditions
270 rather than returning an error status (the fact that they dump
271 diagnostics to stdout during operation should be a clue as to their
272 intended status and usage) */
273
274 #ifdef CONFIG_DEBUG_MALLOC
275
276 #ifdef __WIN32__
277 #include <direct.h>
278 #endif /* __WIN32__ */
279
280 #ifdef __WINCE__
281
282 CHECK_RETVAL_RANGE( 0, MAX_INTLENGTH_STRING ) STDC_NONNULL_ARG( ( 1 ) ) \
wcPrintf(FORMAT_STRING const char * format,...)283 static int wcPrintf( FORMAT_STRING const char *format, ... )
284 {
285 wchar_t wcBuffer[ 1024 + 8 ];
286 char buffer[ 1024 + 8 ];
287 va_list argPtr;
288 int length;
289
290 va_start( argPtr, format );
291 length = vsprintf_s( buffer, 1024, format, argPtr );
292 va_end( argPtr );
293 if( length < 1 )
294 return( length );
295 mbstowcs( wcBuffer, buffer, length + 1 );
296 NKDbgPrintfW( wcBuffer );
297
298 return( length );
299 }
300
301 #define printf wcPrintf
302
303 #endif /* __WINCE__ */
304
305 static int clAllocIndex = 0;
306
clAllocFn(const char * fileName,const char * fnName,const int lineNo,size_t size)307 void *clAllocFn( const char *fileName, const char *fnName,
308 const int lineNo, size_t size )
309 {
310 #ifdef CONFIG_MALLOCTEST
311 static int mallocCount = 0, mallocFailCount = 0;
312 #endif /* CONFIG_MALLOCTEST */
313 #if defined( __WIN32__ ) || defined( __UNIX__ )
314 char buffer[ 512 + 8 ];
315 #endif /* __WIN32__ || __UNIX__ */
316 BYTE *memPtr;
317 int length;
318
319 assert( fileName != NULL );
320 assert( fnName != NULL );
321 assert( lineNo > 0 );
322 assert( size > 0 && size < MAX_INTLENGTH );
323
324 /* Strip off the leading path components if we can to reduce the amount
325 of noise in the output */
326 #if defined( __WIN32__ ) || defined( __UNIX__ )
327 if( getcwd( buffer, 512 ) != NULL )
328 {
329 const int pathLen = strlen( buffer ) + 1; /* Leading path + '/' */
330
331 assert( pathLen < strlen( fileName ) );
332 fileName += pathLen;
333 }
334 #endif /* __WIN32__ || __UNIX__ */
335
336 length = DEBUG_PRINT( "ALLOC: %s:%s:%d", fileName, fnName, lineNo );
337 while( length < 46 )
338 {
339 putchar( ' ' );
340 length++;
341 }
342 DEBUG_PRINT( " %4d - %d bytes.\n", clAllocIndex, size );
343 #ifdef CONFIG_MALLOCTEST
344 /* If we've exceeded the allocation count, make the next attempt to
345 allocate memory fail */
346 if( mallocCount >= mallocFailCount )
347 {
348 mallocCount = 0;
349 mallocFailCount++;
350
351 return( NULL );
352 }
353 mallocCount++;
354 #endif /* CONFIG_MALLOCTEST */
355 if( ( memPtr = malloc( size + sizeof( LONG ) ) ) == NULL )
356 return( NULL );
357 mputLong( memPtr, clAllocIndex ); /* Implicit memPtr += sizeof( LONG ) */
358 clAllocIndex++;
359 return( memPtr );
360 }
361
clFreeFn(const char * fileName,const char * fnName,const int lineNo,void * memblock)362 void clFreeFn( const char *fileName, const char *fnName,
363 const int lineNo, void *memblock )
364 {
365 #if defined( __WIN32__ ) || defined( __UNIX__ )
366 char buffer[ 512 + 8 ];
367 #endif /* __WIN32__ || __UNIX__ */
368 BYTE *memPtr = ( BYTE * ) memblock - sizeof( LONG );
369 int index, length;
370
371 assert( fileName != NULL );
372 assert( fnName != NULL );
373 assert( lineNo > 0 );
374
375 /* Strip off the leading path components if we can to reduce the amount
376 of noise in the output */
377 #if defined( __WIN32__ ) || defined( __UNIX__ )
378 if( getcwd( buffer, 512 ) != NULL )
379 {
380 const int pathLen = strlen( buffer ) + 1; /* Leading path + '/' */
381
382 assert( pathLen < strlen( fileName ) );
383 fileName += pathLen;
384 }
385 #endif /* __WIN32__ || __UNIX__ */
386
387 index = mgetLong( memPtr );
388 memPtr -= sizeof( LONG ); /* mgetLong() changes memPtr */
389 length = DEBUG_PRINT( "FREE : %s:%s:%d", fileName, fnName, lineNo );
390 while( length < 46 )
391 {
392 putchar( ' ' );
393 length++;
394 }
395 DEBUG_PRINT( " %4d.\n", index );
396 free( memPtr );
397 }
398 #endif /* CONFIG_DEBUG_MALLOC */
399
400 /* Fault-testing malloc() that fails after a given number of allocations */
401
402 #ifdef CONFIG_FAULT_MALLOC
403
404 static int currentAllocCount = 0, failAllocCount = 0;
405 static BOOLEAN allocFailed = FALSE;
406
clFaultAllocSetCount(const int number)407 void clFaultAllocSetCount( const int number )
408 {
409 currentAllocCount = 0;
410 failAllocCount = number;
411 allocFailed = FALSE;
412 }
413
clFaultAllocFn(const char * fileName,const char * fnName,const int lineNo,size_t size)414 void *clFaultAllocFn( const char *fileName, const char *fnName,
415 const int lineNo, size_t size )
416 {
417 /* If we've failed an allocation we probably shouldn't get here again,
418 however if we're running a multithreaded init then the second thread
419 could try and allocate memory after the first one has failed */
420 if( allocFailed )
421 {
422 #ifdef __WIN32__
423 DEBUG_PRINT(( "\n<<< Further allocation call from thread %X after "
424 "previous call failed, called from %s line %d in "
425 "%s.>>>\n", GetCurrentThreadId(), fnName, lineNo,
426 fileName ));
427 #else
428 DEBUG_PRINT(( "\n<<< Further allocation call after previous call "
429 "failed, called from %s line %d in %s.>>>\n", fnName,
430 lineNo, fileName ));
431 #endif /* __WIN32__ */
432 if( failAllocCount < 15 )
433 {
434 DEBUG_PRINT(( "<<< (This could be because of a multithreaded "
435 "init).>>>\n" ));
436 DEBUG_PRINT(( "\n" ));
437 }
438 return( NULL );
439 }
440
441 /* If we haven't reached the failure allocation count, return normally */
442 if( currentAllocCount < failAllocCount )
443 {
444 currentAllocCount++;
445 return( malloc( size ) );
446 }
447
448 /* We've reached the failure count, fail the allocation */
449 #ifdef __WIN32__
450 DEBUG_PRINT(( "\n<<< Failing allocation call #%d for thread %X, called "
451 "from %s line %d in %s.>>>\n\n", failAllocCount + 1,
452 GetCurrentThreadId(), fnName, lineNo, fileName ));
453 #else
454 DEBUG_PRINT(( "\n<<< Failing at allocation call #%d, called from %s line "
455 "%d in %s.>>>\n\n", failAllocCount + 1, fnName, lineNo,
456 fileName ));
457 #endif /* __WIN32__ */
458 allocFailed = TRUE;
459 return( NULL );
460 }
461 #endif /* CONFIG_FAULT_MALLOC */
462