📄 sec_mem.c
字号:
/****************************************************************************
* *
* Secure Memory Management *
* Copyright Peter Gutmann 1995-2007 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "acl.h"
#include "kernel.h"
#else
#include "crypt.h"
#include "kernel/acl.h"
#include "kernel/kernel.h"
#endif /* Compiler-specific includes */
/* A pointer to the kernel data block */
static KERNEL_DATA *krnlData = NULL;
/* The minimum and maximum amount of secure memory that we can ever allocate.
A more normal upper bound is 8K, however the SSL session cache constitutes
a single large chunk of secure memory that goes way over this limit */
#define MIN_ALLOC_SIZE 8
#ifdef __MSDOS__
#define MAX_ALLOC_SIZE 8192
#else
#define MAX_ALLOC_SIZE 32768L
#endif /* 16 vs. 32-bit systems */
/* To support page locking we need to store some additional information with
the memory block. We do this by reserving an extra memory block at the
start of the allocated block and saving the information there.
The information stored in the extra block is a flag indicating whether the
block is pagelocked (so we can call the unlock function when we free it),
the size of the block, and pointers to the next and previous pointers in
the list of allocated blocks (this is used by the thread that walks the
block list touching each one) */
#if INT_MAX <= 32767
#define MEMLOCK_HEADERSIZE 16
#elif INT_MAX <= 0xFFFFFFFFUL
#define MEMLOCK_HEADERSIZE 32
#else
#define MEMLOCK_HEADERSIZE 64
#endif /* 16/32/64-bit systems */
/* If it's a debug build we also insert a canary at the start and end of each
block to detect memory overwrites, the block size is adjusted accordingly
to handle this extra data */
#define CANARY_STARTVALUE "\xC0\xED\xBA\xBE" /* More fun than dead beef */
#define CANARY_ENDVALUE "\x36\xDD\x24\x36"
#ifndef NDEBUG
#define adjustMemCanary( size ) \
size += CANARY_SIZE
#define insertMemCanary( memBlockPtr, memPtr ) \
memcpy( memBlockPtr->canary, CANARY_STARTVALUE, CANARY_SIZE ); \
memcpy( memPtr + memBlockPtr->size - CANARY_SIZE, CANARY_ENDVALUE, \
CANARY_SIZE )
#define checkMemCanary( memBlockPtr, memPtr ) \
assert( !memcmp( memBlockPtr->canary, CANARY_STARTVALUE, CANARY_SIZE ) ); \
assert( !memcmp( memPtr + memBlockPtr->size - CANARY_SIZE, \
CANARY_ENDVALUE, CANARY_SIZE ) );
#else
#define adjustMemCanary( size )
#define insertMemCanary( memBlockPtr, memPtr )
#define checkMemCanary( memBlockPtr, memPtr )
#endif /* NDEBUG */
/****************************************************************************
* *
* Misc Functions *
* *
****************************************************************************/
/* Prepare to allocate/free a block of secure memory */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int checkInitAlloc( OUT void **allocPtrPtr,
IN_RANGE( MIN_ALLOC_SIZE, MAX_ALLOC_SIZE ) \
const int size )
{
/* Make sure that the parameters are in order */
if( !isWritePtr( allocPtrPtr, sizeof( void * ) ) )
retIntError();
REQUIRES( size >= MIN_ALLOC_SIZE && size <= MAX_ALLOC_SIZE );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int checkInitFree( void **freePtrPtr, OUT_PTR BYTE **memPtrPtr,
OUT_PTR MEMLOCK_INFO **memBlockPtrPtr )
{
MEMLOCK_INFO *memBlockPtr;
BYTE *memPtr;
PRE( isWritePtr( memPtrPtr, sizeof( BYTE * ) ) );
PRE( isWritePtr( memBlockPtrPtr, sizeof( MEMLOCK_INFO * ) ) );
/* Make sure that the parameters are in order */
if( !isReadPtr( freePtrPtr, sizeof( void * ) ) || \
!isReadPtr( *freePtrPtr, sizeof( MIN_ALLOC_SIZE ) ) )
retIntError();
/* Recover the actual allocated memory block data from the pointer */
memPtr = ( ( BYTE * ) *freePtrPtr ) - MEMLOCK_HEADERSIZE;
if( !isReadPtr( memPtr, sizeof( MEMLOCK_INFO ) ) )
retIntError();
memBlockPtr = ( MEMLOCK_INFO * ) memPtr;
REQUIRES( memBlockPtr->size >= sizeof( MEMLOCK_INFO ) + MIN_ALLOC_SIZE && \
memBlockPtr->size <= sizeof( MEMLOCK_INFO ) + MAX_ALLOC_SIZE && \
( memBlockPtr->isLocked == FALSE || \
memBlockPtr->isLocked == TRUE ) );
*memPtrPtr = memPtr;
*memBlockPtrPtr = memBlockPtr;
return( CRYPT_OK );
}
/* Insert and unlink a memory block from a list of memory blocks. We can't
use insertDoubleListElements()/deleteDoubleListElement() for this because
they don't handle the end-of-list pointer, since they're intended for
random-access lists rather than append-only lists */
STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static void insertMemBlock( INOUT MEMLOCK_INFO **allocatedListHeadPtr,
INOUT MEMLOCK_INFO **allocatedListTailPtr,
INOUT MEMLOCK_INFO *memBlockPtr )
{
MEMLOCK_INFO *allocatedListHead = *allocatedListHeadPtr;
MEMLOCK_INFO *allocatedListTail = *allocatedListTailPtr;
assert( isWritePtr( allocatedListHeadPtr, sizeof( MEMLOCK_INFO * ) ) );
assert( allocatedListHead == NULL || \
isWritePtr( allocatedListHead, sizeof( MEMLOCK_INFO ) ) );
assert( isWritePtr( allocatedListTailPtr, sizeof( MEMLOCK_INFO * ) ) );
assert( allocatedListTail == NULL || \
isWritePtr( allocatedListTail, sizeof( MEMLOCK_INFO ) ) );
assert( isWritePtr( memBlockPtr, sizeof( MEMLOCK_INFO * ) ) );
/* If it's a new list, set up the head and tail pointers and return */
if( allocatedListHead == NULL )
{
*allocatedListHeadPtr = *allocatedListTailPtr = memBlockPtr;
return;
}
/* It's an existing list, add the new element to the end */
allocatedListTail->next = memBlockPtr;
memBlockPtr->prev = allocatedListTail;
*allocatedListTailPtr = memBlockPtr;
}
STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static void unlinkMemBlock( INOUT MEMLOCK_INFO **allocatedListHeadPtr,
INOUT MEMLOCK_INFO **allocatedListTailPtr,
INOUT MEMLOCK_INFO *memBlockPtr )
{
MEMLOCK_INFO *allocatedListHead = *allocatedListHeadPtr;
MEMLOCK_INFO *allocatedListTail = *allocatedListTailPtr;
MEMLOCK_INFO *nextBlockPtr = memBlockPtr->next;
MEMLOCK_INFO *prevBlockPtr = memBlockPtr->prev;
assert( isWritePtr( allocatedListHeadPtr, sizeof( MEMLOCK_INFO * ) ) );
assert( allocatedListHead == NULL || \
isWritePtr( allocatedListHead, sizeof( MEMLOCK_INFO ) ) );
assert( isWritePtr( allocatedListTailPtr, sizeof( MEMLOCK_INFO * ) ) );
assert( allocatedListTail == NULL || \
isWritePtr( allocatedListTail, sizeof( MEMLOCK_INFO ) ) );
assert( isWritePtr( memBlockPtr, sizeof( MEMLOCK_INFO * ) ) );
/* If we're removing the block from the start of the list, make the
start the next block */
if( memBlockPtr == allocatedListHead )
*allocatedListHeadPtr = nextBlockPtr;
else
{
assert( prevBlockPtr != NULL );
/* Delete from the middle or end of the list */
prevBlockPtr->next = nextBlockPtr;
}
if( nextBlockPtr != NULL )
nextBlockPtr->prev = prevBlockPtr;
/* If we're removed the last element, update the end pointer */
if( memBlockPtr == allocatedListTail )
*allocatedListTailPtr = prevBlockPtr;
/* Clear the current block's pointers, just to be clean */
memBlockPtr->next = memBlockPtr->prev = NULL;
}
#if 0 /* Currently unused, in practice would be called from a worker thread
that periodically touches all secure-data pages */
/* Walk the allocated block list touching each page. In most cases we don't
need to explicitly touch the page since the allocated blocks are almost
always smaller than the MMU's page size and simply walking the list
touches them, but in some rare cases we need to explicitly touch each
page */
static void touchAllocatedPages( void )
{
MEMLOCK_INFO *memBlockPtr;
/* Lock the allocation object to ensure that other threads don't try to
access them */
MUTEX_LOCK( allocation );
/* Walk down the list (which implicitly touches each page). If the
allocated region is larger than 4K, explicitly touch each 4K page.
This assumes a page size of 4K which is usually true (and difficult
to determine otherwise), in any case it doesn't make much difference
since nothing ever allocates more than two 4K pages */
for( memBlockPtr = krnlData->allocatedListHead; memBlockPtr != NULL;
memBlockPtr = memBlockPtr->next )
{
const int pageSize = getSysVar( SYSVAR_PAGESIZE );
/* If the allocated region has pages beyond the first one (which
we've already touched by accessing the header), explicitly
touch those pages as well */
if( memBlockPtr->size > pageSize )
{
BYTE *memPtr = ( BYTE * ) memBlockPtr + pageSize;
int memSize = memBlockPtr->size;
/* Touch each page. The rather convoluted expression is to try
and stop it from being optimised away - it always evaluates to
true since we only get here if allocatedListHead != NULL, but
hopefully the compiler won't be able to figure that out */
while( memSize > pageSize )
{
if( *memPtr || krnlData->allocatedListHead != NULL )
memPtr += pageSize;
memSize -= pageSize;
}
}
}
/* Unlock the allocation object to allow access by other threads */
MUTEX_UNLOCK( allocation );
}
#endif /* 0 */
/****************************************************************************
* *
* Init/Shutdown Functions *
* *
****************************************************************************/
/* Create and destroy the secure allocation information */
int initAllocation( KERNEL_DATA *krnlDataPtr )
{
int status;
PRE( isWritePtr( krnlDataPtr, sizeof( KERNEL_DATA ) ) );
/* Set up the reference to the kernel data block */
krnlData = krnlDataPtr;
/* Clear the list head and tail pointers */
krnlData->allocatedListHead = krnlData->allocatedListTail = NULL;
/* Initialize any data structures required to make the allocation thread-
safe */
MUTEX_CREATE( allocation, status );
ENSURES( cryptStatusOK( status ) );
return( CRYPT_OK );
}
void endAllocation( void )
{
/* Destroy any data structures required to make the allocation thread-
safe */
MUTEX_DESTROY( allocation );
krnlData = NULL;
}
/****************************************************************************
* *
* Windows Secure Memory Allocation Functions *
* *
****************************************************************************/
#if defined( __WIN32__ )
#if !defined( NDEBUG ) && !defined( NT_DRIVER ) && !defined( __BORLANDC__ )
#define USE_HEAP_CHECKING
#endif /* Win32 debug version */
#ifdef USE_HEAP_CHECKING
#include <crtdbg.h> /* For heap checking in debug version */
#endif /* USE_HEAP_CHECKING */
/* Get the start address of a page and, given an address in a page and a
size, determine on which page the data ends. These are used to determine
which pages a memory block covers */
#if defined( _MSC_VER ) && ( _MSC_VER >= 1400 )
#define PTR_TYPE INT_PTR
#else
#define PTR_TYPE long
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -