Skip to content

Commit 08ea1d8

Browse files
authored
prevent self-deadlock with PICOLIBC due to calloc/realloc internally calling malloc/free (#2069)
1 parent 7b07b0b commit 08ea1d8

File tree

1 file changed

+41
-24
lines changed

1 file changed

+41
-24
lines changed

src/rp2_common/pico_malloc/malloc.c

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,39 @@ extern void REAL_FUNC(free)(void *mem);
2424

2525
extern char __StackLimit; /* Set by linker. */
2626

27+
#if !PICO_USE_MALLOC_MUTEX
28+
#define MALLOC_ENTER(outer) ((void)0);
29+
#define MALLOC_EXIT(outer) ((void)0);
30+
#elif !__PICOLIBC__
31+
#define MALLOC_ENTER(outer) mutex_enter_blocking(&malloc_mutex);
32+
#define MALLOC_EXIT(outer) mutex_exit(&malloc_mutex);
33+
#else
34+
static uint8_t mutex_exception_level_plus_one[NUM_CORES];
35+
// PICOLIBC implementations of calloc and realloc may call malloc and free,
36+
// so we need to cope with re-entrant calls. We don't want to use a recursive
37+
// mutex as that won't catch usage within an ISR; instead we record the exception
38+
// nesting as of acquiring the mutex
39+
#define MALLOC_ENTER(outer) \
40+
uint exception = __get_current_exception(); \
41+
uint core_num = get_core_num(); \
42+
/* we skip the locking on outer == false if we are in the same irq nesting */ \
43+
/* note: the `+ 1` is to distinguish no malloc nesting vs no-exception/irq level */ \
44+
bool do_lock = outer || exception + 1 != mutex_exception_level_plus_one[core_num]; \
45+
if (do_lock) { \
46+
mutex_enter_blocking(&malloc_mutex); \
47+
if (outer) mutex_exception_level_plus_one[core_num] = (uint8_t)(exception + 1); \
48+
}
49+
50+
#define MALLOC_EXIT(outer) \
51+
if (outer) { \
52+
mutex_exception_level_plus_one[core_num] = 0; \
53+
} \
54+
if (do_lock) { \
55+
mutex_exit(&malloc_mutex); \
56+
}
57+
58+
#endif
59+
2760
static inline void check_alloc(__unused void *mem, __unused uint size) {
2861
#if PICO_MALLOC_PANIC
2962
if (!mem || (((char *)mem) + size) > &__StackLimit) {
@@ -33,13 +66,9 @@ static inline void check_alloc(__unused void *mem, __unused uint size) {
3366
}
3467

3568
void *WRAPPER_FUNC(malloc)(size_t size) {
36-
#if PICO_USE_MALLOC_MUTEX
37-
mutex_enter_blocking(&malloc_mutex);
38-
#endif
69+
MALLOC_ENTER(false)
3970
void *rc = REAL_FUNC(malloc)(size);
40-
#if PICO_USE_MALLOC_MUTEX
41-
mutex_exit(&malloc_mutex);
42-
#endif
71+
MALLOC_EXIT(false)
4372
#if PICO_DEBUG_MALLOC
4473
if (!rc) {
4574
printf("malloc %d failed to allocate memory\n", (uint) size);
@@ -52,13 +81,9 @@ void *WRAPPER_FUNC(malloc)(size_t size) {
5281
}
5382

5483
void *WRAPPER_FUNC(calloc)(size_t count, size_t size) {
55-
#if PICO_USE_MALLOC_MUTEX
56-
mutex_enter_blocking(&malloc_mutex);
57-
#endif
84+
MALLOC_ENTER(true)
5885
void *rc = REAL_FUNC(calloc)(count, size);
59-
#if PICO_USE_MALLOC_MUTEX
60-
mutex_exit(&malloc_mutex);
61-
#endif
86+
MALLOC_EXIT(true)
6287
#if PICO_DEBUG_MALLOC
6388
if (!rc) {
6489
printf("calloc %d failed to allocate memory\n", (uint) (count * size));
@@ -71,13 +96,9 @@ void *WRAPPER_FUNC(calloc)(size_t count, size_t size) {
7196
}
7297

7398
void *WRAPPER_FUNC(realloc)(void *mem, size_t size) {
74-
#if PICO_USE_MALLOC_MUTEX
75-
mutex_enter_blocking(&malloc_mutex);
76-
#endif
99+
MALLOC_ENTER(true)
77100
void *rc = REAL_FUNC(realloc)(mem, size);
78-
#if PICO_USE_MALLOC_MUTEX
79-
mutex_exit(&malloc_mutex);
80-
#endif
101+
MALLOC_EXIT(true)
81102
#if PICO_DEBUG_MALLOC
82103
if (!rc) {
83104
printf("realloc %d failed to allocate memory\n", (uint) size);
@@ -90,11 +111,7 @@ void *WRAPPER_FUNC(realloc)(void *mem, size_t size) {
90111
}
91112

92113
void WRAPPER_FUNC(free)(void *mem) {
93-
#if PICO_USE_MALLOC_MUTEX
94-
mutex_enter_blocking(&malloc_mutex);
95-
#endif
114+
MALLOC_ENTER(false)
96115
REAL_FUNC(free)(mem);
97-
#if PICO_USE_MALLOC_MUTEX
98-
mutex_exit(&malloc_mutex);
99-
#endif
116+
MALLOC_EXIT(false)
100117
}

0 commit comments

Comments
 (0)