Hello!
I've been working for a few months on a bare-metal Kinetis project, using Processor Expert to provide the HAL. I'm using CW MCU 10.5.
What initially appeared to be a bug in my own code (four bytes being assigned random values in the middle of a buffer), actually translated into what looks to me as a bug in the malloc() routine.
/* Second: couldn't find a buffer that would fit. attempt to merge two existing buffers to make do. */ /* nbytes cannot fit into struct m_buff -> size */ if ( MAX_M_BUFF < nbytes ) { return NULL; } p = head; while ( NULL != p ) { if ( NO == p->used ) { struct m_buff* look_ahd = p->lnk; size_t tot_room = p->size; while ((NULL != look_ahd) && (NO == look_ahd->used) && (tot_room < nbytes)) { tot_room += ((size_t)look_ahd->size + sizeof(struct m_buff)); look_ahd = look_ahd->lnk; } if ( tot_room >= nbytes ) { LOCALALLOC(p, nbytes) p->lnk = look_ahd; p->used = YES; p->size = tot_room; #if HEAP_GROWS if ((look_ahd != NULL) && (look_ahd->lnk == NULL)) tail = &p->lnk; #endif #if defined(_REENTRANT) && _REENTRANT __end_critical_region ( malloc_pool_access ); #endif return ((char_t*) p ) + sizeof( struct m_buff ); } } p = p->lnk; }
Basically, if multiple free buffers are merged to satisfy the need for a bigger buffer, the tail will keep pointing to where an old 'buffer' used to be. The effect of this is that, when a new buffer is allocated using sbrk(),
#if HEAP_GROWS *tail = new_buff; new_buff->lnk = NULL; tail = &new_buff->lnk; #else
the instruction at line 2 will write the address of the new buffer right in the middle of a previously allocated area.
This is the current sequence of mallocs/free, I will try to narrow it down to a simpler test-case tomorrow:
a = malloc(8);
b = malloc(16);
free(a);
free(b);
a = malloc(1140);
b = malloc(16);
free(a);
free(b);
a = malloc(200);
b = malloc(16);
free(a);
free(b);
a = malloc(8);
b = malloc(16);
free(a);
free(b);
a = malloc(1140);
b = malloc(16); -> This is when 'tail' overwrites bytes in the middle of my buffer.
Wondering if this is something that the development team is aware of and if they have a fix.
Thanks,
Alex
Solved! Go to Solution.
I've managed to fix this by changing:
if ((look_ahd != NULL) && (look_ahd->lnk == NULL))
tail = &p->lnk;
to
if (look_ahd == NULL)
tail = &p->lnk;
in alloc.c, line 468.
Basically, the case that was missed was if all the existing buckets were merged into a bigger one. This is covered by testing if 'look_ahd' is NULL, thus meaning that there are no more buckets. If there are (look_ahd != NULL), then there's no point in modifying the 'tail' variable as it already points to the correct bucket.
Alex
Guys, any opinion on this? Especially from the CW support team.
Cheers,
Alex
I've managed to fix this by changing:
if ((look_ahd != NULL) && (look_ahd->lnk == NULL))
tail = &p->lnk;
to
if (look_ahd == NULL)
tail = &p->lnk;
in alloc.c, line 468.
Basically, the case that was missed was if all the existing buckets were merged into a bigger one. This is covered by testing if 'look_ahd' is NULL, thus meaning that there are no more buckets. If there are (look_ahd != NULL), then there's no point in modifying the 'tail' variable as it already points to the correct bucket.
Alex
Hi Alex,
Thank you very much for your comments. Just a couple of questions.
Are you using Kinetis? If so, are you using Freescale ARM compiler? or GCC?
Regards,
Carlos
Hi Carlos,
Yes, I am using Kinetis K22F120 and GCC as the compiler. However, given the nature of the problem and the solution I've found, I think the issue is in malloc's handling of merging the free buffers/buckets, as posted above.
Alex