Memory Leak in newlib with _REENT_SMALL

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

Memory Leak in newlib with _REENT_SMALL

Sourceware - newlib list mailing list
All,

 

I have been using newlib-nano as distributed by ARM on their site as part of
their compiler package for cortex-m devices and have had a problem with
stdio and memory leaks.  I then took newlib-3.3.0 and built it myself and I
think there is a bug in the STDIO support causing the leak.  When a new FILE
object is needed, the function __sfp in findfp.c is called.  This code
iterates through a set of "glue" objects which are basically a header and
four FILE objects stored in a linked list.  If any of these are not in use,
the FILE * flags field will be zero and this block of memory is used for the
new FILE object.  If none are found in the list, a function called
__sfmoreglue() is called to create the next block of four FILE objects for
use and one of these are used.  Note, that these are all stored in a list
attached to the __sglue member of the _GLOBAL_REENT reent structure.

 

When the thread using these FILE objects dies, the function _reclaim_reent
is called in the file reent.c  This file calls a function that is referenced
via a function pointer called __cleanup.   This function pointer is set to
point to a function _cleanup_r() when the STDIO system is initialized.  This
happens in __sinit().   The function _cleanup_r() walks through all of the
FILE entries associated with the thread that is going away and calls either
_fflush_r or _fclose_r depending on how newlib was built (more on this
later).

 

The problem is that FILE objects that were created by the thread, are stored
on the _GLOBAL_REENT list via the __sglue member and not on the per thread
list.  Therefore, these FILEs that were created by the thread are never
processed when the thread dies. After calling _cleanup_r() the
reclaim_reent() function then calls _free_r on the memory associated with
the glue blocks, but again since they are attached to the list on the
_GLOBAL_REENT this memory is not freed.

 

The effect is that if you have a program model in an embedded system where a
thread is created to handle a request and then dies, if this thread uses
stdio (e.g. printf) then the application leaks 3 * the size of a (FILE) each
time the thread is created and destroyed.

 

Note, tacking the created glue blocks to the end of the thread specific
reent structure instead of the GLOBAL_REENT_ structure makes this issue go
away.  I suspect this was the intent and line 157 in findfp.c is just in
error.

 

The next issue is that if _LITE_EXIT is set, _fflush_r is called instead of
_ffclose_r from reclaim_reent.  This forces the output to be flushed, but
does not actually close the FILE.  However, since the next thing that
relaim_reent does is free these glue blocks that contain these FILE objects.
The memory that was allocated as buffers that are managed by the FILE object
are never freed.  Here about 1024 per thread is leaked in my case, but I
suspect this would be bigger if I had actually used stdin or stderr which I
did not.

 

So, I think the fix is

 

1. Change line 157 in findfp.c so that

  for (g = &_GLOBAL_REENT->__sglue;; g = g->_next)

becomes

  for (g = &d->__sglue;; g = g->_next)

 

This causes all thread specific FILE objects to be stored on the thread
local structure.

 

2. And change lines 215 through 219 from

#ifdef _LITE_EXIT

  cleanup_func = _fflush_r;

#else

  cleanup_func = _fclose_r;

#endif

 

to

 

cleanup_func = _fclose_r

 

This ensures that when a thread dies the memory is actually freed by calling
_fclose_r.

 

So, this is the first time I have posted on this mailing list. My questions
are .

 

1. Does the above make sense and has any else seen this issue?  Am I
missing something?
2. What do I do to get this fixed?  Do I submit a patch?  Where?

 

Thanks
Jack Griffin