vPortGetHeapStats() in 'heap_5.c' fails when upgrading from 'V10.5.1' to 'V11.1.0'

Hi!

I’ve upgraded a project with two heap regions from FreeRTOS kernel ‘V10.5.1’ to ‘V11.1.0’. Everything works fine except for the call to vPortGetHeapStats() which gives me a hardfault error (which it didn’t in the previous version).

I’ve tested to revert only ‘heap_5.c’ with the old one, and then it works again.

The setup is:

// Setup heap regions.
m_heap_regions[0].pucStartAddress   = (uint8_t*)(&_heap_start);
m_heap_regions[0].xSizeInBytes      = (size_t)((size_t)&_estack - (size_t)&_heap_start);
m_heap_regions[1].pucStartAddress   = m_heap_sdram; // @ 0xC1000000
m_heap_regions[1].xSizeInBytes      = sizeof(m_heap_sdram); // 256 Kb
vPortDefineHeapRegions(m_heap_regions);

There is not much in the heap when I call vPortGetHeapStats() and the loop in the function works until it moves to the heap region in SDRAM. Then the address pointer of pxBlock->pxNextFreeBlock looks tampered. Instead of pointing inside the SDRAM @ 0xC1000000 it is way off, and the block size is not 256 kB. There is nothing allocated in the SDRAM yet as the SRAM heap is not full.

Is my setup of the regions faulty? For now I will settle with using the previous release of ‘heap_5.c’ as it works. The MCU is an STM32H755.

Best regards,
/Martin

Hi @lundholm
As a starting point, you can enable heap overflow checking by setting configENABLE_HEAP_PROTECTOR to 1 in the FreeRTOSConfig.h . V11.1.0 introduced bounds checking and obfuscation to internal heap block pointers in heap_4.c and heap_5.c to help catch pointer corruptions.

Yes, I saw this new flag and tried both enabled and disabled. Same outcome unfortunately. I will check again that Valgrind does not catch any buffer overwrites in my unit tests tomorrow. But I don’t think so and with the previous version of ‘heap_5.c’ it does work.
/Martin

I tried the following example and it seems to work:

#define HEAP_REGION1_SIZE   ( 10 * 1024 )
#define HEAP_REGION2_SIZE   ( 20 * 1024 )

static void prvUpateHeapRegions( void )
{
    static uint8_t heapRegion1[ HEAP_REGION1_SIZE ];
    static uint8_t heapRegion2[ HEAP_REGION2_SIZE ];

    HeapRegion_t heapRegions[]=
    {
        { &( heapRegion1[ 0 ] ), HEAP_REGION1_SIZE },
        { &( heapRegion2[ 0 ] ), HEAP_REGION2_SIZE },
        { NULL, 0 }
    };

    vPortDefineHeapRegions( &( heapRegions[ 0 ] ) );
}

void Task( void * param )
{
    HeapStats_t stats;

    for( ;; )
    {
        vPortGetHeapStats( &( stats ) );
        printf( "xAvailableHeapSpaceInBytes: %u\n", stats.xAvailableHeapSpaceInBytes );

        vTaskDelay( pdMS_TO_TICKS( 1000 ) );
    }
}

Are you adding the end marker { NULL, 0 } in the array passed to vPortDefineHeapRegions? If yes, can you try to help debug what is causing this corruption? It may be a bug in the heap code and if so, we would like to fix that.

The heap region array is ended with the end marker.
I did an experiment where I set the first heap region as size of 1 kB and the second as 256 kB. My app allocates ~50 kB at startup, so it forces the memory manager to use the second heap. Then the vPortGetHeapStats() works fine! So vPortGetHeapStats() only crashes when there is no allocation in the second heap. As the pvPortMalloc() and vPortFree() works fine, there ought to be a difference in how blocks are handled compared to in vPortGetHeapStats().
My initial thought was that it is due to the address of the second heap starting at 0xC1000000. Maybe a sign extension or something in heapPROTECT_BLOCK_POINTER(), but the code in vPortGetHeapStats() is not that complicated and I really can’t see any such thing. Also, it crashes when configENABLE_HEAP_PROTECTOR is disabled and then the macro does nothing. Maybe I need to run it in assembler code…
I will first check if I put two heaps in the lower RAM and see if I get the same problem.

I change the sizes in my example like the following to ensure that there is no allocation from the second block:

#define HEAP_REGION1_SIZE   ( 1024 * 1024 )
#define HEAP_REGION2_SIZE   ( 30 * 1024 )

It still seems to work fine. Can you also try with the latest main code once?

If I put both heap regions as 2 x 128 kB adjacent blocks in the lower address RAM (0x30000000), then vPortGetHeapStats() works fine. So, I guess it’s either the distance between the heap regions or the heap region address of 0xC1000000 which causes the crash.
I’ll see if I can spot the cause with the debugger.

Btw, what did you mean with “latest main code”? I did clone the most recent FreeRTOS-Kernel yesterday and copied that ‘heap_5.c’ in to my project. The last change of the ‘heap_5.c’ is dated at 2024-11-13.

That is what I meant. All good.

After debugging for a while, I’m pretty convinced it’s my own code which overwrites the end block of the first heap region. It’s adjacent to the _estack marker from the link script, which is used by the _sbrk() function. It’s probably fluke of “luck” that it works with the older ‘heap_5.c’. I’ll be back…

It was a flaw in the linker script which was inherited from STM Cube to put the CPU stack at the end of the SRAM. Well, I rewrote the _sbrk() to simply use malloc() (for sprintf’s) instead of the STM Cube generated area in the linker script, but it wasn’t really the culprit.
So after some rearrangement it now works fine with the latest ‘heap_5.c’ :slight_smile: Not sure why it ever worked before, but I’m just happy to have found the actual reason for the problem.
I really appreciate your response! Thank you very much!

Thank you for sharing your solution!