Memory Leak in newlib with _REENT_SMALL
Butch Griffin via Newlib <[email protected]>
| Newsgroups | gmane.comp.lib.newlib |
|---|---|
| Message-ID | <[email protected]> |
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