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