6666#define heapSIZE_MAX ( ~( ( size_t ) 0 ) )
6767
6868/* Check if multiplying a and b will result in overflow. */
69- #define heapMULTIPLY_WILL_OVERFLOW ( a , b ) \
70- ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
69+ #define heapMULTIPLY_WILL_OVERFLOW ( a , b ) ( ( ( a ) > 0 ) && ( ( b ) > ( heapSIZE_MAX / ( a ) ) ) )
7170
7271/* Check if adding a and b will result in overflow. */
73- #define heapADD_WILL_OVERFLOW ( a , b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
72+ #define heapADD_WILL_OVERFLOW ( a , b ) ( ( a ) > ( heapSIZE_MAX - ( b ) ) )
73+
74+ /* Check if the subtraction operation ( a - b ) will result in underflow. */
75+ #define heapSUBTRACT_WILL_UNDERFLOW ( a , b ) ( ( a ) < ( b ) )
7476
7577/* MSB of the xBlockSize member of an BlockLink_t structure is used to track
7678 * the allocation status of a block. When MSB of the xBlockSize member of
@@ -108,6 +110,38 @@ typedef struct A_BLOCK_LINK
108110 size_t xBlockSize ; /**< The size of the free block. */
109111} BlockLink_t ;
110112
113+ /* Setting configENABLE_HEAP_PROTECTOR to 1 enables heap block pointers
114+ * protection using an application supplied canary value to catch heap
115+ * corruption should a heap buffer overflow occur.
116+ */
117+ #if ( configENABLE_HEAP_PROTECTOR == 1 )
118+
119+ /**
120+ * @brief Application provided function to get a random value to be used as canary.
121+ *
122+ * @param pxHeapCanary [out] Output parameter to return the canary value.
123+ */
124+ extern void vApplicationGetRandomHeapCanary ( portPOINTER_SIZE_TYPE * pxHeapCanary );
125+
126+ /* Canary value for protecting internal heap pointers. */
127+ PRIVILEGED_DATA static portPOINTER_SIZE_TYPE xHeapCanary ;
128+
129+ /* Macro to load/store BlockLink_t pointers to memory. By XORing the
130+ * pointers with a random canary value, heap overflows will result
131+ * in randomly unpredictable pointer values which will be caught by
132+ * heapVALIDATE_BLOCK_POINTER assert. */
133+ #define heapPROTECT_BLOCK_POINTER ( pxBlock ) ( ( BlockLink_t * ) ( ( ( portPOINTER_SIZE_TYPE ) ( pxBlock ) ) ^ xHeapCanary ) )
134+ #else
135+
136+ #define heapPROTECT_BLOCK_POINTER ( pxBlock ) ( pxBlock )
137+
138+ #endif /* configENABLE_HEAP_PROTECTOR */
139+
140+ /* Assert that a heap block pointer is within the heap bounds. */
141+ #define heapVALIDATE_BLOCK_POINTER ( pxBlock ) \
142+ configASSERT( ( ( uint8_t * ) ( pxBlock ) >= &( ucHeap[ 0 ] ) ) && \
143+ ( ( uint8_t * ) ( pxBlock ) <= &( ucHeap[ configTOTAL_HEAP_SIZE - 1 ] ) ) )
144+
111145/*-----------------------------------------------------------*/
112146
113147/*
@@ -221,13 +255,14 @@ void * pvPortMalloc( size_t xWantedSize )
221255 /* Traverse the list from the start (lowest address) block until
222256 * one of adequate size is found. */
223257 pxPreviousBlock = & xStart ;
224- pxBlock = xStart .pxNextFreeBlock ;
258+ pxBlock = heapPROTECT_BLOCK_POINTER ( xStart .pxNextFreeBlock );
259+ heapVALIDATE_BLOCK_POINTER ( pxBlock );
225260
226- while ( ( pxBlock -> xBlockSize < xWantedSize ) &&
227- ( pxBlock -> pxNextFreeBlock != NULL ) )
261+ while ( ( pxBlock -> xBlockSize < xWantedSize ) && ( pxBlock -> pxNextFreeBlock != heapPROTECT_BLOCK_POINTER ( NULL ) ) )
228262 {
229263 pxPreviousBlock = pxBlock ;
230- pxBlock = pxBlock -> pxNextFreeBlock ;
264+ pxBlock = heapPROTECT_BLOCK_POINTER ( pxBlock -> pxNextFreeBlock );
265+ heapVALIDATE_BLOCK_POINTER ( pxBlock );
231266 }
232267
233268 /* If the end marker was reached then a block of adequate size
@@ -236,18 +271,18 @@ void * pvPortMalloc( size_t xWantedSize )
236271 {
237272 /* Return the memory space pointed to - jumping over the
238273 * BlockLink_t structure at its start. */
239- pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock
240- -> pxNextFreeBlock ) +
241- xHeapStructSize );
274+ pvReturn = ( void * ) ( ( ( uint8_t * ) heapPROTECT_BLOCK_POINTER ( pxPreviousBlock -> pxNextFreeBlock ) ) + xHeapStructSize );
275+ heapVALIDATE_BLOCK_POINTER ( pvReturn );
242276
243277 /* This block is being returned for use so must be taken out
244278 * of the list of free blocks. */
245279 pxPreviousBlock -> pxNextFreeBlock = pxBlock -> pxNextFreeBlock ;
246280
247281 /* If the block is larger than required it can be split into
248282 * two. */
249- if ( ( pxBlock -> xBlockSize - xWantedSize ) >
250- heapMINIMUM_BLOCK_SIZE )
283+ configASSERT ( heapSUBTRACT_WILL_UNDERFLOW ( pxBlock -> xBlockSize , xWantedSize ) == 0 );
284+
285+ if ( ( pxBlock -> xBlockSize - xWantedSize ) > heapMINIMUM_BLOCK_SIZE )
251286 {
252287 /* This block is to be split into two. Create a new
253288 * block following the number of bytes requested. The
@@ -265,9 +300,8 @@ void * pvPortMalloc( size_t xWantedSize )
265300 pxBlock -> xBlockSize = xWantedSize ;
266301
267302 /* Insert the new block into the list of free blocks. */
268- pxNewBlockLink -> pxNextFreeBlock = pxPreviousBlock
269- -> pxNextFreeBlock ;
270- pxPreviousBlock -> pxNextFreeBlock = pxNewBlockLink ;
303+ pxNewBlockLink -> pxNextFreeBlock = pxPreviousBlock -> pxNextFreeBlock ;
304+ pxPreviousBlock -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxNewBlockLink );
271305 }
272306 else
273307 {
@@ -343,6 +377,7 @@ void vPortFree( void * pv )
343377 /* This casting is to keep the compiler from issuing warnings. */
344378 pxLink = ( void * ) puc ;
345379
380+ heapVALIDATE_BLOCK_POINTER ( pxLink );
346381 configASSERT ( heapBLOCK_IS_ALLOCATED ( pxLink ) != 0 );
347382 configASSERT ( pxLink -> pxNextFreeBlock == NULL );
348383
@@ -355,12 +390,14 @@ void vPortFree( void * pv )
355390 heapFREE_BLOCK ( pxLink );
356391#if ( configHEAP_CLEAR_MEMORY_ON_FREE == 1 )
357392 {
358- ( void ) memset ( puc + xHeapStructSize ,
359- 0 ,
360- pxLink -> xBlockSize - xHeapStructSize );
393+ /* Check for underflow as this can occur if xBlockSize is
394+ * overwritten in a heap block. */
395+ if ( heapSUBTRACT_WILL_UNDERFLOW ( pxLink -> xBlockSize , xHeapStructSize ) == 0 )
396+ {
397+ ( void ) memset ( puc + xHeapStructSize , 0 , pxLink -> xBlockSize - xHeapStructSize );
398+ }
361399 }
362400#endif
363-
364401 vTaskSuspendAll ();
365402 {
366403 /* Add this block to the list of free blocks. */
@@ -440,9 +477,15 @@ static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
440477
441478 pucAlignedHeap = ( uint8_t * ) uxAddress ;
442479
480+ #if ( configENABLE_HEAP_PROTECTOR == 1 )
481+ {
482+ vApplicationGetRandomHeapCanary ( & ( xHeapCanary ) );
483+ }
484+ #endif
485+
443486 /* xStart is used to hold a pointer to the first item in the list of free
444487 * blocks. The void cast is used to prevent compiler warnings. */
445- xStart .pxNextFreeBlock = ( void * ) pucAlignedHeap ;
488+ xStart .pxNextFreeBlock = ( void * ) heapPROTECT_BLOCK_POINTER ( pucAlignedHeap ) ;
446489 xStart .xBlockSize = ( size_t ) 0 ;
447490
448491 /* pxEnd is used to mark the end of the list of free blocks and is inserted
@@ -452,15 +495,13 @@ static void prvHeapInit( void ) /* PRIVILEGED_FUNCTION */
452495 uxAddress &= ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK );
453496 pxEnd = ( BlockLink_t * ) uxAddress ;
454497 pxEnd -> xBlockSize = 0 ;
455- pxEnd -> pxNextFreeBlock = NULL ;
498+ pxEnd -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( NULL ) ;
456499
457500 /* To start with there is a single free block that is sized to take up the
458501 * entire heap space, minus the space taken by pxEnd. */
459502 pxFirstFreeBlock = ( BlockLink_t * ) pucAlignedHeap ;
460- pxFirstFreeBlock -> xBlockSize = ( size_t ) ( uxAddress -
461- ( portPOINTER_SIZE_TYPE )
462- pxFirstFreeBlock );
463- pxFirstFreeBlock -> pxNextFreeBlock = pxEnd ;
503+ pxFirstFreeBlock -> xBlockSize = ( size_t ) ( uxAddress - ( portPOINTER_SIZE_TYPE ) pxFirstFreeBlock );
504+ pxFirstFreeBlock -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxEnd );
464505
465506 /* Only one block exists - and it covers the entire usable heap space. */
466507 xMinimumEverFreeBytesRemaining = pxFirstFreeBlock -> xBlockSize ;
@@ -476,12 +517,16 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
476517
477518 /* Iterate through the list until a block is found that has a higher address
478519 * than the block being inserted. */
479- for ( pxIterator = & xStart ; pxIterator -> pxNextFreeBlock < pxBlockToInsert ;
480- pxIterator = pxIterator -> pxNextFreeBlock )
520+ for ( pxIterator = & xStart ; heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) < pxBlockToInsert ; pxIterator = heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) )
481521 {
482522 /* Nothing to do here, just iterate to the right position. */
483523 }
484524
525+ if ( pxIterator != & xStart )
526+ {
527+ heapVALIDATE_BLOCK_POINTER ( pxIterator );
528+ }
529+
485530 /* Do the block being inserted, and the block it is being inserted after
486531 * make a contiguous block of memory? */
487532 puc = ( uint8_t * ) pxIterator ;
@@ -500,20 +545,17 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
500545 * make a contiguous block of memory? */
501546 puc = ( uint8_t * ) pxBlockToInsert ;
502547
503- if ( ( puc + pxBlockToInsert -> xBlockSize ) ==
504- ( uint8_t * ) pxIterator -> pxNextFreeBlock )
548+ if ( ( puc + pxBlockToInsert -> xBlockSize ) == ( uint8_t * ) heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) )
505549 {
506- if ( pxIterator -> pxNextFreeBlock != pxEnd )
550+ if ( heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock ) != pxEnd )
507551 {
508552 /* Form one big block from the two blocks. */
509- pxBlockToInsert -> xBlockSize += pxIterator -> pxNextFreeBlock
510- -> xBlockSize ;
511- pxBlockToInsert -> pxNextFreeBlock = pxIterator -> pxNextFreeBlock
512- -> pxNextFreeBlock ;
553+ pxBlockToInsert -> xBlockSize += heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock )-> xBlockSize ;
554+ pxBlockToInsert -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxIterator -> pxNextFreeBlock )-> pxNextFreeBlock ;
513555 }
514556 else
515557 {
516- pxBlockToInsert -> pxNextFreeBlock = pxEnd ;
558+ pxBlockToInsert -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxEnd ) ;
517559 }
518560 }
519561 else
@@ -527,7 +569,7 @@ static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert ) /* PRIVI
527569 * to itself. */
528570 if ( pxIterator != pxBlockToInsert )
529571 {
530- pxIterator -> pxNextFreeBlock = pxBlockToInsert ;
572+ pxIterator -> pxNextFreeBlock = heapPROTECT_BLOCK_POINTER ( pxBlockToInsert ) ;
531573 }
532574 else
533575 {
@@ -545,7 +587,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
545587
546588 vTaskSuspendAll ();
547589 {
548- pxBlock = xStart .pxNextFreeBlock ;
590+ pxBlock = heapPROTECT_BLOCK_POINTER ( xStart .pxNextFreeBlock ) ;
549591
550592 /* pxBlock will be NULL if the heap has not been initialised. The heap
551593 * is initialised automatically when the first allocation is made. */
@@ -569,7 +611,7 @@ void vPortGetHeapStats( HeapStats_t * pxHeapStats )
569611
570612 /* Move to the next block in the chain until the last block is
571613 * reached. */
572- pxBlock = pxBlock -> pxNextFreeBlock ;
614+ pxBlock = heapPROTECT_BLOCK_POINTER ( pxBlock -> pxNextFreeBlock ) ;
573615 }
574616 }
575617 }
0 commit comments