Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

call to calloc() deadlocks with clang/picolibc #1947

Open
geurtv opened this issue Sep 24, 2024 · 2 comments
Open

call to calloc() deadlocks with clang/picolibc #1947

geurtv opened this issue Sep 24, 2024 · 2 comments
Assignees
Milestone

Comments

@geurtv
Copy link
Contributor

geurtv commented Sep 24, 2024

With clang 18.1.3 (from LLVM-embedded-toolchain-for-Arm) the calloc function in picolibc (so that's __real_calloc) calls malloc, which then deadlocks because __wrap_malloc tries to lock the mutex already owned by __wrap_calloc:

calloc == __wrap_calloc:
    lock mutex
    call __real_calloc

__real_calloc:
    call malloc

malloc == __wrap_malloc:
    lock mutex
    << deadlock >>

My work-around is to replace __wrap_calloc's implementation with a call to __wrap_malloc and then a memset:

void *WRAPPER_FUNC(calloc)(size_t count, size_t size) {
    size *= count;
    void* ptr = WRAPPER_FUNC(malloc)(size);
    memset(ptr, 0, size);
    return ptr;
}

NOTE 1: malloc+memset to replace calloc cannot be used because clang (and also gcc) will replace that code with a call to calloc.
NOTE 2: picolibc sources say malloc already 'sets to zero', but it's probably not smart to rely on that.

@kilograham kilograham self-assigned this Sep 24, 2024
@kilograham kilograham added this to the 2.0.1 milestone Sep 24, 2024
@geurtv
Copy link
Contributor Author

geurtv commented Sep 25, 2024

This issue also applies to realloc for which I don't have a work-around, except not locking the mutex in __wrap_realloc.

@geurtv
Copy link
Contributor Author

geurtv commented Oct 2, 2024

Safest fix might be to change malloc_mutex into a recursive mutex. Then it will always just work, also if an unlikely future version of newlib changes its implementation. Otherwise something like this can be done:

#ifndef __PICOLIBC__

auto_init_mutex(malloc_mutex);
#define malloc_mutex_enter_blocking mutex_enter_blocking
#define malloc_mutex_exit           mutex_exit

#else

auto_init_recursive_mutex(malloc_mutex);
#define malloc_mutex_enter_blocking recursive_mutex_enter_blocking
#define malloc_mutex_exit           recursive_mutex_exit

#endif

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants