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