Skip to content

Commit 3888ffb

Browse files
author
Oliver Lavery
committed
configENABLE_HEAP_PROTECTOR implemented in heap_5.c
1 parent 8212ffa commit 3888ffb

File tree

1 file changed

+99
-21
lines changed

1 file changed

+99
-21
lines changed

portable/MemMang/heap_5.c

Lines changed: 99 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@
104104
/* Check if adding a and b will result in overflow. */
105105
#define heapADD_WILL_OVERFLOW( a, b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
106106

107+
/* Check if subtracting a from b will result in underflow. */
108+
#define heapSUBTRACT_WILL_UNDERFLOW( a, b ) ( ( a ) > ( b ) )
109+
107110
/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
108111
* the allocation status of a block. When MSB of the xBlockSize member of
109112
* an BlockLink_t structure is set then the block belongs to the application.
@@ -124,6 +127,38 @@ typedef struct A_BLOCK_LINK
124127
size_t xBlockSize; /**< The size of the free block. */
125128
} BlockLink_t;
126129

130+
/* Enabling configENABLE_HEAP_PROTECTOR provides a security enhanced version of heap_4.c that
131+
* implements pointer protection using a canary value to reduce the risk of code execution
132+
* should a heap buffer overflow vulnerability occur.
133+
* It also implements additional arithmetic and bounds checks on heap values.
134+
*/
135+
#if ( configENABLE_HEAP_PROTECTOR == 1 )
136+
/* We require xApplicationGetRandomNumber in order to initialize our canary value */
137+
extern BaseType_t xApplicationGetRandomNumber( uint32_t * pulNumber );
138+
/* Canary value for protecting internal heap pointers */
139+
static portPOINTER_SIZE_TYPE xHeapCanary;
140+
/* Highest and lowest heap addresses for bounds checking */
141+
static void * pxHeapHighAddress = NULL;
142+
static void * pxHeapLowAddress = NULL;
143+
144+
/* Macro to load/store pxNextFreeBlock pointers to memory. By XORing the
145+
* pointers with a random canary value, intentional heap overwrites will
146+
* result in randomly unpredictable pointer values. If configASSERT() is defined
147+
* out of bound values will result in a safe abend due to bounds checks. */
148+
#define heapPROTECT_POINTER( pxNextFreeBlock ) ( ( struct A_BLOCK_LINK *) ( ( ( portPOINTER_SIZE_TYPE ) ( pxNextFreeBlock ) ) ^ xHeapCanary ) )
149+
/* Macro to perform a bounds check of heap pointers such that a heap pointer that
150+
* falls outside of ucHeap will trigger an assertion. For heap_5.c we check that
151+
* pointers are greater than the lowest address in the lowest region, and less than
152+
* the highest address in the highest region. */
153+
#define heapPROTECT_BOUNDS_CHECK( pxBlock ) configASSERT( pxHeapHighAddress && pxHeapLowAddress && \
154+
( ( void * ) ( pxBlock ) >= pxHeapLowAddress ) && ( ( void * ) ( pxBlock ) < pxHeapHighAddress ) )
155+
#else
156+
157+
#define heapPROTECT_POINTER( pxNextFreeBlock ) ( pxNextFreeBlock )
158+
#define heapPROTECT_BOUNDS_CHECK( pxBlock ) do {} while(0)
159+
160+
#endif /* configENABLE_HEAP_PROTECTOR */
161+
127162
/*-----------------------------------------------------------*/
128163

129164
/*
@@ -217,12 +252,14 @@ void * pvPortMalloc( size_t xWantedSize )
217252
/* Traverse the list from the start (lowest address) block until
218253
* one of adequate size is found. */
219254
pxPreviousBlock = &xStart;
220-
pxBlock = xStart.pxNextFreeBlock;
255+
pxBlock = heapPROTECT_POINTER( xStart.pxNextFreeBlock );
256+
heapPROTECT_BOUNDS_CHECK( pxBlock );
221257

222-
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
258+
while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != heapPROTECT_POINTER( NULL ) ) )
223259
{
224260
pxPreviousBlock = pxBlock;
225-
pxBlock = pxBlock->pxNextFreeBlock;
261+
pxBlock = heapPROTECT_POINTER( pxBlock->pxNextFreeBlock );
262+
heapPROTECT_BOUNDS_CHECK( pxBlock );
226263
}
227264

228265
/* If the end marker was reached then a block of adequate size
@@ -231,21 +268,25 @@ void * pvPortMalloc( size_t xWantedSize )
231268
{
232269
/* Return the memory space pointed to - jumping over the
233270
* BlockLink_t structure at its start. */
234-
pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
271+
pvReturn = ( void * ) ( ( uint8_t * ) heapPROTECT_POINTER( pxPreviousBlock->pxNextFreeBlock ) ) + xHeapStructSize;
272+
heapPROTECT_BOUNDS_CHECK( pvReturn );
235273

236274
/* This block is being returned for use so must be taken out
237275
* of the list of free blocks. */
238276
pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
239277

240278
/* If the block is larger than required it can be split into
241-
* two. */
279+
* two. Also check for underflow as this can occur if xBlockSize is
280+
* overwritten in a heap block */
281+
configASSERT( !heapSUBTRACT_WILL_UNDERFLOW( xWantedSize, pxBlock->xBlockSize ) );
242282
if( ( pxBlock->xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
243283
{
244284
/* This block is to be split into two. Create a new
245285
* block following the number of bytes requested. The void
246286
* cast is used to prevent byte alignment warnings from the
247287
* compiler. */
248288
pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
289+
configASSERT( ( ( ( size_t ) pxNewBlockLink ) & portBYTE_ALIGNMENT_MASK ) == 0 );
249290

250291
/* Calculate the sizes of two blocks split from the
251292
* single block. */
@@ -254,7 +295,7 @@ void * pvPortMalloc( size_t xWantedSize )
254295

255296
/* Insert the new block into the list of free blocks. */
256297
pxNewBlockLink->pxNextFreeBlock = pxPreviousBlock->pxNextFreeBlock;
257-
pxPreviousBlock->pxNextFreeBlock = pxNewBlockLink;
298+
pxPreviousBlock->pxNextFreeBlock = heapPROTECT_POINTER( pxNewBlockLink );
258299
}
259300
else
260301
{
@@ -310,6 +351,7 @@ void * pvPortMalloc( size_t xWantedSize )
310351
}
311352
#endif /* if ( configUSE_MALLOC_FAILED_HOOK == 1 ) */
312353

354+
configASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) portBYTE_ALIGNMENT_MASK ) == 0 );
313355
return pvReturn;
314356
}
315357
/*-----------------------------------------------------------*/
@@ -327,7 +369,8 @@ void vPortFree( void * pv )
327369

328370
/* This casting is to keep the compiler from issuing warnings. */
329371
pxLink = ( void * ) puc;
330-
372+
/* This will assert if pv is out of heap range */
373+
heapPROTECT_BOUNDS_CHECK( pxLink );
331374
configASSERT( heapBLOCK_IS_ALLOCATED( pxLink ) != 0 );
332375
configASSERT( pxLink->pxNextFreeBlock == NULL );
333376

@@ -340,6 +383,9 @@ void vPortFree( void * pv )
340383
heapFREE_BLOCK( pxLink );
341384
#if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
342385
{
386+
/* Check for underflow as this can occur if xBlockSize is
387+
* overwritten in a heap block */
388+
configASSERT( !heapSUBTRACT_WILL_UNDERFLOW( xHeapStructSize, pxLink->xBlockSize ) );
343389
( void ) memset( puc + xHeapStructSize, 0, pxLink->xBlockSize - xHeapStructSize );
344390
}
345391
#endif
@@ -403,12 +449,18 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )
403449
BlockLink_t * pxIterator;
404450
uint8_t * puc;
405451

452+
/* Ensure the block to insert is within the heap region */
453+
heapPROTECT_BOUNDS_CHECK( pxBlockToInsert );
454+
406455
/* Iterate through the list until a block is found that has a higher address
407456
* than the block being inserted. */
408-
for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
457+
for( pxIterator = &xStart; heapPROTECT_POINTER( pxIterator->pxNextFreeBlock ) < pxBlockToInsert; pxIterator = heapPROTECT_POINTER( pxIterator->pxNextFreeBlock ) )
409458
{
410459
/* Nothing to do here, just iterate to the right position. */
411460
}
461+
if ( pxIterator != &xStart ) {
462+
heapPROTECT_BOUNDS_CHECK( pxIterator );
463+
}
412464

413465
/* Do the block being inserted, and the block it is being inserted after
414466
* make a contiguous block of memory? */
@@ -428,17 +480,17 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )
428480
* make a contiguous block of memory? */
429481
puc = ( uint8_t * ) pxBlockToInsert;
430482

431-
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
483+
if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) heapPROTECT_POINTER( pxIterator->pxNextFreeBlock ) )
432484
{
433-
if( pxIterator->pxNextFreeBlock != pxEnd )
485+
if( heapPROTECT_POINTER( pxIterator->pxNextFreeBlock ) != pxEnd )
434486
{
435487
/* Form one big block from the two blocks. */
436-
pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
437-
pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
488+
pxBlockToInsert->xBlockSize += heapPROTECT_POINTER( pxIterator->pxNextFreeBlock )->xBlockSize;
489+
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_POINTER( pxIterator->pxNextFreeBlock )->pxNextFreeBlock;
438490
}
439491
else
440492
{
441-
pxBlockToInsert->pxNextFreeBlock = pxEnd;
493+
pxBlockToInsert->pxNextFreeBlock = heapPROTECT_POINTER( pxEnd );
442494
}
443495
}
444496
else
@@ -452,7 +504,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )
452504
* to itself. */
453505
if( pxIterator != pxBlockToInsert )
454506
{
455-
pxIterator->pxNextFreeBlock = pxBlockToInsert;
507+
pxIterator->pxNextFreeBlock = heapPROTECT_POINTER( pxBlockToInsert );
456508
}
457509
else
458510
{
@@ -470,10 +522,30 @@ void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
470522
BaseType_t xDefinedRegions = 0;
471523
portPOINTER_SIZE_TYPE xAddress;
472524
const HeapRegion_t * pxHeapRegion;
525+
#if ( configENABLE_HEAP_PROTECTOR == 1)
526+
uint32_t xRandom;
527+
#endif
473528

474529
/* Can only call once! */
475530
configASSERT( pxEnd == NULL );
476531

532+
#if ( configENABLE_HEAP_PROTECTOR == 1)
533+
switch( sizeof( portPOINTER_SIZE_TYPE ) ) {
534+
case 2:
535+
case 4:
536+
xApplicationGetRandomNumber( &xRandom );
537+
xHeapCanary = ( portPOINTER_SIZE_TYPE ) xRandom;
538+
break;
539+
default:
540+
case 8:
541+
xApplicationGetRandomNumber( &xRandom );
542+
xHeapCanary = ( ( portPOINTER_SIZE_TYPE ) xRandom ) << sizeof( uint32_t ) * heapBITS_PER_BYTE;
543+
xApplicationGetRandomNumber( &xRandom );
544+
xHeapCanary |= ( portPOINTER_SIZE_TYPE ) xRandom;
545+
break;
546+
}
547+
#endif
548+
477549
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
478550

479551
while( pxHeapRegion->xSizeInBytes > 0 )
@@ -499,14 +571,17 @@ void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
499571
{
500572
/* xStart is used to hold a pointer to the first item in the list of
501573
* free blocks. The void cast is used to prevent compiler warnings. */
502-
xStart.pxNextFreeBlock = ( BlockLink_t * ) xAlignedHeap;
574+
xStart.pxNextFreeBlock = ( BlockLink_t * ) heapPROTECT_POINTER( xAlignedHeap );
503575
xStart.xBlockSize = ( size_t ) 0;
576+
#if ( configENABLE_HEAP_PROTECTOR == 1 )
577+
pxHeapLowAddress = ( void * ) xAlignedHeap;
578+
#endif
504579
}
505580
else
506581
{
507582
/* Should only get here if one region has already been added to the
508583
* heap. */
509-
configASSERT( pxEnd != NULL );
584+
configASSERT( pxEnd != heapPROTECT_POINTER( NULL ) );
510585

511586
/* Check blocks are passed in with increasing start addresses. */
512587
configASSERT( ( size_t ) xAddress > ( size_t ) pxEnd );
@@ -523,24 +598,27 @@ void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions )
523598
xAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
524599
pxEnd = ( BlockLink_t * ) xAddress;
525600
pxEnd->xBlockSize = 0;
526-
pxEnd->pxNextFreeBlock = NULL;
601+
pxEnd->pxNextFreeBlock = heapPROTECT_POINTER( NULL );
527602

528603
/* To start with there is a single free block in this region that is
529604
* sized to take up the entire heap region minus the space taken by the
530605
* free block structure. */
531606
pxFirstFreeBlockInRegion = ( BlockLink_t * ) xAlignedHeap;
532607
pxFirstFreeBlockInRegion->xBlockSize = ( size_t ) ( xAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlockInRegion );
533-
pxFirstFreeBlockInRegion->pxNextFreeBlock = pxEnd;
608+
pxFirstFreeBlockInRegion->pxNextFreeBlock = heapPROTECT_POINTER( pxEnd );
534609

535610
/* If this is not the first region that makes up the entire heap space
536611
* then link the previous region to this region. */
537612
if( pxPreviousFreeBlock != NULL )
538613
{
539-
pxPreviousFreeBlock->pxNextFreeBlock = pxFirstFreeBlockInRegion;
614+
pxPreviousFreeBlock->pxNextFreeBlock = heapPROTECT_POINTER( pxFirstFreeBlockInRegion );
540615
}
541616

542617
xTotalHeapSize += pxFirstFreeBlockInRegion->xBlockSize;
543618

619+
#if ( configENABLE_HEAP_PROTECTOR == 1 )
620+
pxHeapHighAddress = ( ( uint8_t * )pxFirstFreeBlockInRegion ) + pxFirstFreeBlockInRegion->xBlockSize;
621+
#endif
544622
/* Move onto the next HeapRegion_t structure. */
545623
xDefinedRegions++;
546624
pxHeapRegion = &( pxHeapRegions[ xDefinedRegions ] );
@@ -561,7 +639,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
561639

562640
vTaskSuspendAll();
563641
{
564-
pxBlock = xStart.pxNextFreeBlock;
642+
pxBlock = heapPROTECT_POINTER( xStart.pxNextFreeBlock );
565643

566644
/* pxBlock will be NULL if the heap has not been initialised. The heap
567645
* is initialised automatically when the first allocation is made. */
@@ -591,7 +669,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
591669

592670
/* Move to the next block in the chain until the last block is
593671
* reached. */
594-
pxBlock = pxBlock->pxNextFreeBlock;
672+
pxBlock = heapPROTECT_POINTER( pxBlock->pxNextFreeBlock );
595673
}
596674
}
597675
}

0 commit comments

Comments
 (0)