nuttx/mm/mm_heap/mm_free.c

242 lines
6.6 KiB
C
Raw Normal View History

/****************************************************************************
* mm/mm_heap/mm_free.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <assert.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/sched.h>
#include <nuttx/mm/mm.h>
#include <nuttx/mm/kasan.h>
#include "mm_heap/mm.h"
/****************************************************************************
* Private Functions
****************************************************************************/
static void add_delaylist(FAR struct mm_heap_s *heap, FAR void *mem)
{
#if defined(CONFIG_BUILD_FLAT) || defined(__KERNEL__)
FAR struct mm_delaynode_s *tmp = mem;
irqstate_t flags;
/* Delay the deallocation until a more appropriate time. */
flags = up_irq_save();
# ifdef CONFIG_DEBUG_ASSERTIONS
FAR struct mm_freenode_s *node;
node = (FAR struct mm_freenode_s *)((FAR char *)mem - MM_SIZEOF_ALLOCNODE);
DEBUGASSERT(MM_NODE_IS_ALLOC(node));
# endif
tmp->flink = heap->mm_delaylist[this_cpu()];
heap->mm_delaylist[this_cpu()] = tmp;
#if CONFIG_MM_FREE_DELAYCOUNT_MAX > 0
heap->mm_delaycount[this_cpu()]++;
#endif
up_irq_restore(flags);
#endif
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mm_delayfree
*
* Description:
* Delay free memory if `delay` is true, otherwise free it immediately.
*
****************************************************************************/
void mm_delayfree(FAR struct mm_heap_s *heap, FAR void *mem, bool delay)
{
FAR struct mm_freenode_s *node;
FAR struct mm_freenode_s *prev;
FAR struct mm_freenode_s *next;
size_t nodesize;
size_t prevsize;
if (mm_lock(heap) < 0)
{
/* Meet -ESRCH return, which means we are in situations
* during context switching(See mm_lock() & gettid()).
* Then add to the delay list.
*/
add_delaylist(heap, mem);
return;
}
#ifdef CONFIG_MM_FILL_ALLOCATIONS
memset(mem, MM_FREE_MAGIC, mm_malloc_size(heap, mem));
#endif
kasan_poison(mem, mm_malloc_size(heap, mem));
mm_heap/kasan: poison free node after return back the heap list The free node is still in use after kasan_poison(), the node member access will cause the assert report by kasan. | (gdb) bt | #0 kasan_report (addr=1743265406637584896, size=140737337053680, is_write=46) at kasan/kasan.c:97 | #1 0x0000555555607bdd in __asan_loadN_noabort (addr=140737272831420, size=4) at kasan/kasan.c:289 | #2 0x0000555555607cd7 in __asan_load4_noabort (addr=140737272831420) at kasan/kasan.c:323 | #3 0x00005555556061ef in gmtime_r (timep=0x7ffff3275dbc, result=0x7ffff3275e10) at time/lib_gmtimer.c:301 | #4 0x000055555560e507 in sim_rtc_rdtime (lower=0x55555576b780 <g_sim_rtc>, rtctime=0x7ffff3275e10) at sim/up_rtc.c:77 | #5 0x00005555555fcbdb in up_rtc_gettime (tp=0x7ffff3275ef0) at timers/arch_rtc.c:128 | #6 0x00005555555f08b4 in clock_systime_timespec (ts=0x7ffff3275ef0) at clock/clock_systime_timespec.c:72 | #7 0x00005555555ecc77 in note_common (tcb=0x7ffff31d2180, note=0x7ffff3275f80, length=21 '\025', type=18 '\022') at sched/sched_note.c:144 | #8 0x00005555555ed706 in sched_note_syscall_enter (nr=1, argc=0) at sched/sched_note.c:765 | #9 0x000055555560eb37 in __wrap_getpid () at wraps/WRAP_getpid.c:26 | #10 0x0000555555608d1c in mm_takesemaphore (heap=0x7ffff30ae000) at mm_heap/mm_sem.c:127 | #11 0x0000555555609477 in mm_free (heap=0x7ffff30ae000, mem=0x7ffff3265b80) at mm_heap/mm_free.c:89 | #12 0x00005555556070c5 in free (mem=0x7ffff3265b80) at umm_heap/umm_free.c:49 | #13 0x000055555560c3b0 in up_release_stack (dtcb=0x7ffff31e4b00, ttype=0 '\000') at sim/up_releasestack.c:67 | #14 0x00005555555f2515 in nxsched_release_tcb (tcb=0x7ffff31e4b00, ttype=0 '\000') at sched/sched_releasetcb.c:134 | #15 0x00005555556bdf0c in nxtask_terminate (pid=4, nonblocking=true) at task/task_terminate.c:184 | #16 0x00005555556bdb0f in nxtask_exit () at task/task_exit.c:168 | #17 0x000055555566e05f in up_exit (status=0) at sim/up_exit.c:64 | #18 0x000055555564f454 in _exit (status=0) at task/exit.c:78 | #19 0x000055555560ea89 in __wrap__exit (parm1=0) at wraps/WRAP__exit.c:27 | #20 0x00005555555eb288 in exit (status=0) at stdlib/lib_exit.c:54 | #21 0x00005555555fe2cc in nxtask_startup (entrypt=0x555555670c34 <critmon_start_main>, argc=1, argv=0x7ffff3265bb0) at sched/task_startup.c:70 | #22 0x00005555555f02a0 in nxtask_start () at task/task_start.c:134 | #23 0x0000000000000000 in ?? () Signed-off-by: chao.an <anchao@xiaomi.com>
2022-08-01 10:33:48 +02:00
if (delay)
{
mm_unlock(heap);
add_delaylist(heap, mem);
return;
}
/* Map the memory chunk into a free node */
node = (FAR struct mm_freenode_s *)((FAR char *)mem - MM_SIZEOF_ALLOCNODE);
nodesize = MM_SIZEOF_NODE(node);
Merged in paimonen/nuttx/pullreq_libc_libnx_updates (pull request #757) Pullreq libc libnx updates * NuttX: make strerror() return 'Success' for 0 * NuttX: fix strrchr() so that it considers null terminator as part of string From strrchr(3) man page: "The terminating null byte is considered part of the string, so that if c is specified as '\0', these functions return a pointer to the terminator." * NuttX: mm_free(): Add DEBUGASSERT()'s to catch memory corruption early. It's easier to find the source when asserts fail already when freeing an overflowed buffer, than if the corruption is only detected on next malloc(). * MM_FILL_ALLOCATIONS: Add debug option to fill all mallocs() This is helpful for detecting uninitialized variables, especially in C++ code. I seem to be forgetting to initialize member variables and then they just get random values.. * NuttX: nxtk_bitmapwindow: Fix warning message when bitmap is fully off-screen. * nxfonts_getfont: Avoid unnecessary warnings for other whitespace chars also. * NuttX: Fix kerning of 'I' in Sans17x22 font The I character was running together with some other characters, e.g. in sequence "IMI". * NXMU: Revalidate window pointer for mouse events. NXMU caches the previous window pointer so that further mouse events can be sent to the same window. However, if the window is destroyed while mouse button is held down, the pointer may become invalid and cause a crash. This patch revalidates the pointer before using it. Approved-by: GregoryN <gnutt@nuttx.org>
2018-11-12 16:36:35 +01:00
/* Sanity check against double-frees */
DEBUGASSERT(MM_NODE_IS_ALLOC(node));
Merged in paimonen/nuttx/pullreq_libc_libnx_updates (pull request #757) Pullreq libc libnx updates * NuttX: make strerror() return 'Success' for 0 * NuttX: fix strrchr() so that it considers null terminator as part of string From strrchr(3) man page: "The terminating null byte is considered part of the string, so that if c is specified as '\0', these functions return a pointer to the terminator." * NuttX: mm_free(): Add DEBUGASSERT()'s to catch memory corruption early. It's easier to find the source when asserts fail already when freeing an overflowed buffer, than if the corruption is only detected on next malloc(). * MM_FILL_ALLOCATIONS: Add debug option to fill all mallocs() This is helpful for detecting uninitialized variables, especially in C++ code. I seem to be forgetting to initialize member variables and then they just get random values.. * NuttX: nxtk_bitmapwindow: Fix warning message when bitmap is fully off-screen. * nxfonts_getfont: Avoid unnecessary warnings for other whitespace chars also. * NuttX: Fix kerning of 'I' in Sans17x22 font The I character was running together with some other characters, e.g. in sequence "IMI". * NXMU: Revalidate window pointer for mouse events. NXMU caches the previous window pointer so that further mouse events can be sent to the same window. However, if the window is destroyed while mouse button is held down, the pointer may become invalid and cause a crash. This patch revalidates the pointer before using it. Approved-by: GregoryN <gnutt@nuttx.org>
2018-11-12 16:36:35 +01:00
node->size &= ~MM_ALLOC_BIT;
/* Update heap statistics */
heap->mm_curused -= nodesize;
/* Check if the following node is free and, if so, merge it */
next = (FAR struct mm_freenode_s *)((FAR char *)node + nodesize);
DEBUGASSERT(MM_PREVNODE_IS_ALLOC(next));
if (MM_NODE_IS_FREE(next))
{
FAR struct mm_allocnode_s *andbeyond;
size_t nextsize = MM_SIZEOF_NODE(next);
/* Get the node following the next node (which will
* become the new next node). We know that we can never
* index past the tail chunk because it is always allocated.
*/
andbeyond = (FAR struct mm_allocnode_s *)((FAR char *)next + nextsize);
DEBUGASSERT(MM_PREVNODE_IS_FREE(andbeyond) &&
andbeyond->preceding == nextsize);
/* Remove the next node. There must be a predecessor,
* but there may not be a successor node.
*/
DEBUGASSERT(next->blink);
next->blink->flink = next->flink;
if (next->flink)
{
next->flink->blink = next->blink;
}
/* Then merge the two chunks */
nodesize += nextsize;
node->size = nodesize | (node->size & MM_MASK_BIT);
andbeyond->preceding = nodesize;
next = (FAR struct mm_freenode_s *)andbeyond;
}
else
{
next->size |= MM_PREVFREE_BIT;
next->preceding = nodesize;
}
/* Check if the preceding node is also free and, if so, merge
* it with this node
*/
if (MM_PREVNODE_IS_FREE(node))
{
prev = (FAR struct mm_freenode_s *)
((FAR char *)node - node->preceding);
prevsize = MM_SIZEOF_NODE(prev);
DEBUGASSERT(MM_NODE_IS_FREE(prev) && node->preceding == prevsize);
/* Remove the node. There must be a predecessor, but there may
* not be a successor node.
*/
DEBUGASSERT(prev->blink);
prev->blink->flink = prev->flink;
if (prev->flink)
{
prev->flink->blink = prev->blink;
}
/* Then merge the two chunks */
prevsize += nodesize;
prev->size = prevsize | (prev->size & MM_MASK_BIT);
next->preceding = prevsize;
node = prev;
}
/* Add the merged node to the nodelist */
mm_addfreechunk(heap, node);
mm_unlock(heap);
}
/****************************************************************************
* Name: mm_free
*
* Description:
* Returns a chunk of memory to the list of free nodes, merging with
* adjacent free chunks if possible.
*
****************************************************************************/
void mm_free(FAR struct mm_heap_s *heap, FAR void *mem)
{
minfo("Freeing %p\n", mem);
/* Protect against attempts to free a NULL reference */
if (mem == NULL)
{
return;
}
DEBUGASSERT(mm_heapmember(heap, mem));
#ifdef CONFIG_MM_HEAP_MEMPOOL
if (heap->mm_mpool)
{
if (mempool_multiple_free(heap->mm_mpool, mem) >= 0)
{
return;
}
}
#endif
mm_delayfree(heap, mem, CONFIG_MM_FREE_DELAYCOUNT_MAX > 0);
}