📄 sec_mem.c
字号:
/****************************************************************************
* *
* Secure Memory Management *
* Copyright Peter Gutmann 1995-2005 *
* *
****************************************************************************/
#include <stdlib.h>
#if defined( INC_ALL )
#include "crypt.h"
#include "acl.h"
#include "kernel.h"
#elif defined( INC_CHILD )
#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
#define MAX_ALLOC_SIZE 65536L
/* 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.
These macros have portability problems since they assume that
sizeof( long ) == sizeof( void * ), but there's no easy way to avoid this
since for some strange reason C doesn't allow the perfectly sensible use
of logical operations on addresses */
#if defined( __WIN32__ )
/* This assumes Intel hardware, which is virtually always the case */
#define getPageSize() 4096
#elif defined( __UNIX__ )
#if defined( __hpux ) || defined( _M_XENIX ) || defined( __aux )
#define getPageSize() 4096
#else
#define getPageSize() getpagesize()
#endif /* Unix variant-specific brokenness */
#endif /* OS-specifc page size determination */
#define getPageStartAddress( address ) \
( ( long ) ( address ) & ~( getPageSize() - 1 ) )
#define getPageEndAddress( address, size ) \
getPageStartAddress( ( long ) address + ( size ) - 1 )
/* 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 */
/* Insert and unlink a memory block from a list of memory blocks */
#define insertMemBlock( allocatedListHead, allocatedListTail, memBlockPtr ) \
if( allocatedListHead == NULL ) \
allocatedListHead = allocatedListTail = memBlockPtr; \
else \
{ \
allocatedListTail->next = memBlockPtr; \
memBlockPtr->prev = allocatedListTail; \
allocatedListTail = memBlockPtr; \
}
#define unlinkMemBlock( allocatedListHead, allocatedListTail, memBlockPtr ) \
{ \
MEMLOCK_INFO *nextBlockPtr, *prevBlockPtr; \
\
nextBlockPtr = memBlockPtr->next; \
prevBlockPtr = memBlockPtr->prev; \
if( memBlockPtr == allocatedListHead ) \
allocatedListHead = nextBlockPtr; \
else \
prevBlockPtr->next = nextBlockPtr; \
if( nextBlockPtr != NULL ) \
nextBlockPtr->prev = prevBlockPtr; \
if( memBlockPtr == allocatedListTail ) \
allocatedListTail = prevBlockPtr; \
}
/* Prepare to allocate/free a block of secure memory */
#define checkInitAlloc( ptr, size ) \
if( !isWritePtr( ptr, sizeof( void * ) ) || \
( size ) < MIN_ALLOC_SIZE || ( size ) > MAX_ALLOC_SIZE ) \
{ \
assert( NOTREACHED ); \
return( CRYPT_ERROR_MEMORY ); \
} \
*( ptr ) = NULL; \
#define checkInitFree( ptr, memPtr, memBlockPtr ) \
if( !isReadPtr( ptr, sizeof( void * ) ) || \
!isReadPtr( *( ptr ), sizeof( MIN_ALLOC_SIZE ) ) ) \
{ \
assert( NOTREACHED ); \
return; \
} \
memPtr = ( ( BYTE * ) *( ptr ) ) - MEMLOCK_HEADERSIZE; \
if( !isReadPtr( memPtr, sizeof( MEMLOCK_INFO ) ) ) \
{ \
assert( NOTREACHED ); \
return; \
} \
memBlockPtr = ( MEMLOCK_INFO * ) memPtr; \
if( memBlockPtr->size < sizeof( MEMLOCK_INFO ) + MIN_ALLOC_SIZE || \
memBlockPtr->size > sizeof( MEMLOCK_INFO ) + MAX_ALLOC_SIZE || \
( memBlockPtr->isLocked != FALSE && \
memBlockPtr->isLocked != TRUE ) ) \
{ \
assert( NOTREACHED ); \
return; \
}
/****************************************************************************
* *
* Misc Functions *
* *
****************************************************************************/
#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 )
{
if( memBlockPtr->size > 4096 )
{
BYTE *memPtr = ( BYTE * ) memBlockPtr + 4096;
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 > 4096 )
{
if( *memPtr || krnlData->allocatedListHead != NULL )
memPtr += 4096;
memSize -= 4096;
}
}
}
/* Unlock the allocation object to allow access by other threads */
MUTEX_UNLOCK( allocation );
}
#endif /* 0 */
#if 0 /* 9/3/04 No longer needed since the kernel tracks allocated obj.data */
/* Determine the size of a krnlMemalloc()'d memory block */
int krnlMemsize( const void *pointer )
{
MEMLOCK_INFO *memBlockPtr;
BYTE *memPtr = ( BYTE * ) pointer;
/* Make sure that it's a valid pointer */
if( !isReadPtr( memPtr, sizeof( MEMLOCK_INFO ) ) )
{
assert( NOTREACHED );
return( 0 );
}
/* Find out how big the memory block is */
memPtr -= MEMLOCK_HEADERSIZE;
memBlockPtr = ( MEMLOCK_INFO * ) memPtr;
/* Make sure that nothing's overwritten our memory */
assert( !memcmp( memBlockPtr->canary, CANARY_STARTVALUE, CANARY_SIZE ) );
assert( !memcmp( memPtr + memBlockPtr->size - CANARY_SIZE,
CANARY_ENDVALUE, CANARY_SIZE ) );
return( memBlockPtr->size - MEMLOCK_HEADERSIZE );
}
#endif /* 0 */
/****************************************************************************
* *
* Init/Shutdown Functions *
* *
****************************************************************************/
/* Create and destroy the secure allocation information */
int initAllocation( KERNEL_DATA *krnlDataPtr )
{
/* 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 );
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 )
#include <crtdbg.h> /* For heap checking in debug version */
#endif /* Win32 debug version */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -