6565#define heapSIZE_MAX ( ~( ( size_t ) 0 ) )
6666
6767/* Check if multiplying a and b will result in overflow. */
68- #define heapMULTIPLY_WILL_OVERFLOW ( a , b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
68+ #define heapMULTIPLY_WILL_OVERFLOW ( a , b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
6969
7070/* Check if adding a and b will result in overflow. */
71- #define heapADD_WILL_OVERFLOW ( a , b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
71+ #define heapADD_WILL_OVERFLOW ( a , b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
72+
73+ /* Check if the subtraction operation ( a - b ) will result in underflow. */
74+ #define heapSUBTRACT_WILL_UNDERFLOW ( a , b ) ( ( a ) < ( b ) )
7275
7376/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
7477 * the allocation status of a block. When MSB of the xBlockSize member of
@@ -100,6 +103,38 @@ typedef struct A_BLOCK_LINK
100103 size_t xBlockSize ; /**< The size of the free block. */
101104} BlockLink_t ;
102105
106+ /* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers
107+ * protection using an application supplied canary value to catch heap
108+ * corruption should a heap buffer overflow occur.
109+ */
110+ #if ( configENABLE_HEAP_PROTECTOR == 1 )
111+
112+ /**
113+ * @brief Application provided function to get a random value to be used as canary.
114+ *
115+ * @param pxHeapCanary [out] Output parameter to return the canary value.
116+ */
117+ extern void vApplicationGetRandomHeapCanary ( portPOINTER_SIZE_TYPE * pxHeapCanary );
118+
119+ /* Canary value for protecting internal heap pointers. */
120+ PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary ;
121+
122+ /* Macro to load/store BlockLink_t pointers to memory. By XORing the
123+ * pointers with a random canary value, heap overflows will result
124+ * in randomly unpredictable pointer values which will be caught by
125+ * heapVALIDATE_BLOCK_POINTER assert. */
126+ #define heapPROTECT_BLOCK_POINTER ( pxBlock ) ( ( BlockLink_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ( pxBlock ) ) ^ xHeapCanary ) )
127+ #else
128+
129+ #define heapPROTECT_BLOCK_POINTER ( pxBlock ) ( pxBlock )
130+
131+ #endif /* configENABLE_HEAP_PROTECTOR */
132+
133+ /* Assert that a heap block pointer is within the heap bounds. */
134+ #define heapVALIDATE_BLOCK_POINTER ( pxBlock ) \
135+ configASSERT( ( ( uint8_t * ) ( pxBlock ) >= &( ucHeap[ 0 ] ) ) && \
136+ ( ( uint8_t * ) ( pxBlock ) <= &( ucHeap[ configTOTAL_HEAP_SIZE - 1 ] ) ) )
137+
103138/*-----------------------------------------------------------*/
104139
105140/*
@@ -206,12 +241,14 @@ void * pvPortMalloc( size_t xWantedSize )
206241 /* Traverse the list from the start (lowest address) block until
207242 * one of adequate size is found. */
208243 pxPreviousBlock = & xStart ;
209- pxBlock = xStart .pxNextFreeBlock ;
244+ pxBlock = heapPROTECT_BLOCK_POINTER ( xStart .pxNextFreeBlock );
245+ heapVALIDATE_BLOCK_POINTER ( pxBlock );
210246
211- while ( ( pxBlock -> xBlockSize < xWantedSize ) && ( pxBlock -> pxNextFreeBlock != NULL ) )
247+ while ( ( pxBlock -> xBlockSize < xWantedSize ) && ( pxBlock -> pxNextFreeBlock != heapPROTECT_BLOCK_POINTER ( NULL ) ) )
212248 {
213249 pxPreviousBlock = pxBlock ;
214- pxBlock = pxBlock -> pxNextFreeBlock ;
250+ pxBlock = heapPROTECT_BLOCK_POINTER ( pxBlock -> pxNextFreeBlock );
251+ heapVALIDATE_BLOCK_POINTER ( pxBlock );
215252 }
216253
217254 /* If the end marker was reached then a block of adequate size
@@ -220,14 +257,17 @@ void * pvPortMalloc( size_t xWantedSize )
220257 {
221258 /* Return the memory space pointed to - jumping over the
222259 * BlockLink_t structure at its start. */
223- pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock -> pxNextFreeBlock ) + xHeapStructSize );
260+ pvReturn = ( void * ) ( ( ( uint8_t * ) heapPROTECT_BLOCK_POINTER ( pxPreviousBlock -> pxNextFreeBlock ) ) + xHeapStructSize );
261+ heapVALIDATE_BLOCK_POINTER ( pvReturn );
224262
225263 /* This block is being returned for use so must be taken out
226264 * of the list of free blocks. */
227265 pxPreviousBlock -> pxNextFreeBlock = pxBlock -> pxNextFreeBlock ;
228266
229267 /* If the block is larger than required it can be split into
230268 * two. */
269+ configASSERT ( heapSUBTRACT_WILL_UNDERFLOW ( pxBlock -> xBlockSize , xWantedSize ) == 0 );
270+
231271 if ( ( pxBlock -> xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
232272 {
233273 /* This block is to be split into two. Create a new
@@ -244,7 +284,7 @@ void * pvPortMalloc( size_t xWantedSize )
244284
245285 /* Insert the new block into the list of free blocks. */
246286 pxNewBlockLink -> pxNextFreeBlock = pxPreviousBlock -> pxNextFreeBlock ;
247- pxPreviousBlock -> pxNextFreeBlock = pxNewBlockLink ;
287+ pxPreviousBlock -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxNewBlockLink ) ;
248288 }
249289 else
250290 {
@@ -319,6 +359,7 @@ void vPortFree( void * pv )
319359 /* This casting is to keep the compiler from issuing warnings. */
320360 pxLink = ( void * ) puc ;
321361
362+ heapVALIDATE_BLOCK_POINTER ( pxLink );
322363 configASSERT ( heapBLOCK_IS_ALLOCATED ( pxLink ) != 0 );
323364 configASSERT ( pxLink -> pxNextFreeBlock == NULL );
324365
@@ -331,7 +372,12 @@ void vPortFree( void * pv )
331372 heapFREE_BLOCK ( pxLink );
332373 #if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
333374 {
334- ( void ) memset ( puc + xHeapStructSize , 0 , pxLink -> xBlockSize - xHeapStructSize );
375+ /* Check for underflow as this can occur if xBlockSize is
376+ * overwritten in a heap block. */
377+ if ( heapSUBTRACT_WILL_UNDERFLOW ( pxLink -> xBlockSize , xHeapStructSize ) == 0 )
378+ {
379+ ( void ) memset ( puc + xHeapStructSize , 0 , pxLink -> xBlockSize - xHeapStructSize );
380+ }
335381 }
336382 #endif
337383
@@ -414,9 +460,15 @@ static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
414460
415461 pucAlignedHeap = ( uint8_t * ) uxAddress ;
416462
463+ #if ( configENABLE_HEAP_PROTECTOR == 1 )
464+ {
465+ vApplicationGetRandomHeapCanary ( & ( xHeapCanary ) );
466+ }
467+ #endif
468+
417469 /* xStart is used to hold a pointer to the first item in the list of free
418470 * blocks. The void cast is used to prevent compiler warnings. */
419- xStart .pxNextFreeBlock = ( void * ) pucAlignedHeap ;
471+ xStart .pxNextFreeBlock = ( void * ) heapPROTECT_BLOCK_POINTER ( pucAlignedHeap ) ;
420472 xStart .xBlockSize = ( size_t ) 0 ;
421473
422474 /* pxEnd is used to mark the end of the list of free blocks and is inserted
@@ -426,13 +478,13 @@ static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
426478 uxAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
427479 pxEnd = ( BlockLink_t * ) uxAddress ;
428480 pxEnd -> xBlockSize = 0 ;
429- pxEnd -> pxNextFreeBlock = NULL ;
481+ pxEnd -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( NULL ) ;
430482
431483 /* To start with there is a single free block that is sized to take up the
432484 * entire heap space, minus the space taken by pxEnd. */
433485 pxFirstFreeBlock = ( BlockLink_t * ) pucAlignedHeap ;
434486 pxFirstFreeBlock -> xBlockSize = ( size_t ) ( uxAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock );
435- pxFirstFreeBlock -> pxNextFreeBlock = pxEnd ;
487+ pxFirstFreeBlock -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxEnd ) ;
436488
437489 /* Only one block exists - and it covers the entire usable heap space. */
438490 xMinimumEverFreeBytesRemaining = pxFirstFreeBlock -> xBlockSize ;
@@ -447,11 +499,16 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
447499
448500 /* Iterate through the list until a block is found that has a higher address
449501 * than the block being inserted. */
450- for ( pxIterator = & xStart ; pxIterator -> pxNextFreeBlock < pxBlockToInsert ; pxIterator = pxIterator -> pxNextFreeBlock )
502+ for ( pxIterator = & xStart ; heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) < pxBlockToInsert ; pxIterator = heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) )
451503 {
452504 /* Nothing to do here, just iterate to the right position. */
453505 }
454506
507+ if ( pxIterator != & xStart )
508+ {
509+ heapVALIDATE_BLOCK_POINTER ( pxIterator );
510+ }
511+
455512 /* Do the block being inserted, and the block it is being inserted after
456513 * make a contiguous block of memory? */
457514 puc = ( uint8_t * ) pxIterator ;
@@ -470,17 +527,17 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
470527 * make a contiguous block of memory? */
471528 puc = ( uint8_t * ) pxBlockToInsert ;
472529
473- if ( ( puc + pxBlockToInsert -> xBlockSize ) == ( uint8_t * ) pxIterator -> pxNextFreeBlock )
530+ if ( ( puc + pxBlockToInsert -> xBlockSize ) == ( uint8_t * ) heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) )
474531 {
475- if ( pxIterator -> pxNextFreeBlock != pxEnd )
532+ if ( heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) != pxEnd )
476533 {
477534 /* Form one big block from the two blocks. */
478- pxBlockToInsert -> xBlockSize += pxIterator -> pxNextFreeBlock -> xBlockSize ;
479- pxBlockToInsert -> pxNextFreeBlock = pxIterator -> pxNextFreeBlock -> pxNextFreeBlock ;
535+ pxBlockToInsert -> xBlockSize += heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) -> xBlockSize ;
536+ pxBlockToInsert -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) -> pxNextFreeBlock ;
480537 }
481538 else
482539 {
483- pxBlockToInsert -> pxNextFreeBlock = pxEnd ;
540+ pxBlockToInsert -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxEnd ) ;
484541 }
485542 }
486543 else
@@ -494,7 +551,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
494551 * to itself. */
495552 if ( pxIterator != pxBlockToInsert )
496553 {
497- pxIterator -> pxNextFreeBlock = pxBlockToInsert ;
554+ pxIterator -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxBlockToInsert ) ;
498555 }
499556 else
500557 {
@@ -510,7 +567,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
510567
511568 vTaskSuspendAll ();
512569 {
513- pxBlock = xStart .pxNextFreeBlock ;
570+ pxBlock = heapPROTECT_BLOCK_POINTER ( xStart .pxNextFreeBlock ) ;
514571
515572 /* pxBlock will be NULL if the heap has not been initialised. The heap
516573 * is initialised automatically when the first allocation is made. */
@@ -534,7 +591,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
534591
535592 /* Move to the next block in the chain until the last block is
536593 * reached. */
537- pxBlock = pxBlock -> pxNextFreeBlock ;
594+ pxBlock = heapPROTECT_BLOCK_POINTER ( pxBlock -> pxNextFreeBlock ) ;
538595 }
539596 }
540597 }
0 commit comments