Esp32 malloc corrupt the heap

Hello, I have a small test program for mallo but it fail:

bool alloc(unsigned long int i){
  Serial.printf("\r\n i = %lu\r\n", i);
  Serial.printf("GetFreeHeap avant alloc: %lu\r\n",heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
  static void *workBuff = heap_caps_malloc(i, MALLOC_CAP_8BIT);
  if (workBuff != NULL){
    Serial.printf("GetAllocHeap après malloc: %lu\r\n", heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
   heap_caps_free(workBuff);
    Serial.printf("GetFreeHeap après free: %lu\r\n", heap_caps_get_largest_free_block(MALLOC_CAP_8BIT));
    return true; 
  } 
  Serial.println("Allocation non réussie!");
  return false;
}

void setup() {
   Serial.begin(115200);
  while(!Serial){
   ;
  }
  Serial.println("Test begin"); 
}

void loop() {
  unsigned long i = 1;
  if (alloc(i)){
    Serial.println("ok");
  }
}

As you can see the loop call the method alloc with the number of byte to allocate.
After two loop the heap is corrupted after the free instruction.
Why ?

Many thanks for your help
Best regards

Your topic was MOVED to its current forum category as it is more suitable than the original

It is often told that garbage collection doesn't work for many microcontrollers.

This line calls heap_caps_malloc once and assigns the return value to workBuff, and then workBuff retains that value across calls to alloc:

You're trying to free the same allocation more than once which leads to heap corruption. Try removing static.

Hello chrisop,
I don't understand your reply.
In the alloc method i call malloc and free the alllocation.
Why did you see " You're trying to free the same allocation more than once which leads to heap corruption. Try removing static"

Why removing static word ?

regards

I disagree. workBuff gets set by a call to malloc. If non-null, it gets immediately freed. Next time around, the same repeats. I see no problem there.

But I am curious - How does OP KNOW the heap is getting corrupted? What is printed out. What happens when it "dies"? Keep in mind, some Serial output may be lost when it dies, unless you do a Serial.flush after each print.

hello,
Here is the log result:
Test begin

i = 1
GetFreeHeap avant alloc: 118772
GetAllocHeap après malloc: 118772
GetFreeHeap après free: 118772
ok

i = 1
GetFreeHeap avant alloc: 118772
GetAllocHeap après malloc: 118772
CORRUPT HEAP: Bad head at 0x3ffb917c. Expected 0xabba1234 got 0x3ffb8014

assert failed: multi_heap_free multi_heap_poisoning.c:253 (head != NULL)

Backtrace:0x40082f71:0x3ffb26300x40088e61:0x3ffb2650 0x4008d9a9:0x3ffb2670 0x4008d60f:0x3ffb27a0 0x40083429:0x3ffb27c0 0x400d10d7:0x3ffb27e0 0x400d1151:0x3ffb2800 0x400d1949:0x3ffb2820

The problem is that workBuff retains its value starting at the very first call to alloc. Its value never changes after it's set the first time. The important thing to note is that malloc is called only once. The memory is freed in alloc, and then the next time alloc is called, workBuff is already non-null, so it's freed again (which corrupts the heap because the memory is not allocated again).

@RayLivingston: Here's a C++ test program (not Arduino) to illustrate what I mean. The function once is used in place of heap_caps_malloc:

#include <iostream>

int once()
{
	std::cout << "This prints once!" << std::endl;
	return 42;
}

void loop()
{
	static int x = once();
	std::cout << "x is " << x << std::endl;
}

int main()
{
	for (int i = 0; i < 5; ++i) {
		loop();
	}
	return 0;
}

Output:

This prints once!
x is 42
x is 42
x is 42
x is 42
x is 42

What it retains (holds) is the pointer address, not the allocated buffer, may be the terminology was confusing. There is no notion of retain (count like in objective C) for this but yes the initialization only happens once hence the crash

It’s also a good practice to set a pointer to nullptr after freeing the data it pointed at.

Ah! I see your point. You're right. Of course, the "fix" is simple:

static void *workBuff = null;
workBuff = heap_caps_malloc(i, MALLOC_CAP_8BIT);

The static really does nothing useful, but it also does no harm, other than one pointers worth of memory being allocated on the heap.

Personally, I would also always do this:

heap_caps_free(workBuff);
workBuff = null;

Yes, that's what I meant. I used "retain" in the general meaning of the word (continue to have (something); keep possession of), rather than any specific meaning in another language. Thanks for the clarification.

This output also proves the point christop was making. Note the free memory NEVER changes.

I had to read your text twice as indeed for some reason I thought you were saying the free did not really free the memory as it was retained elsewhere.(which is a concept in some other languages for objects)

But on second read I got it. Good catch.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.