-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
What type of defect/bug is this?
Crash or memory corruption (segv, abort, etc...)
How can the issue be reproduced?
We found the memory usage by the application is keep growing in our environment,
so we suspected there were some memory leak in the application.
To verify it, I ran the application with Xcode instrument for detection in my machine.
Following is the summary that generated with the help of AI.
Please help verify it, thanks.
Memory Leak: rad_vp2data Thread-Local Buffer
Applies to: macOS and Linux (and any platform where TLS_STORAGE_CLASS / __THREAD is used; see src/include/threads.h).
Summary
A memory leak (~90 KB in Xcode Instruments) was traced to the thread-local buffer used by rad_vp2data() in the attribute/xlat expansion path. The potential fix ensures that every worker thread registers its buffer with a pthread key when it first allocates, so the destructor runs at thread exit and the buffer is freed.
Leak Call Path (Xcode Instruments)
| Layer | Symbol | Library/Binary |
|---|---|---|
| Thread | thread_start → _pthread_start |
libsystem_pthread.dylib |
| Request | request_handler_thread → proxy_running → request_finish → rad_postauth → process_post_auth |
radiusd |
| Module | indexed_modcall → modcall → modcall_recurse → modcall_child (recursive) |
radiusd |
| Map/xlat | map_to_request → map_to_vp → radius_axlat_struct → xlat_expand_struct → xlat_process → xlat_aprint → xlat_string |
libfreeradius-server.dylib |
| Allocation | rad_vp2data → _malloc_zone_malloc_instrumented_or_legacy |
libfreeradius-radius.dylib → libsystem_malloc.dylib |
The leak occurs during post-auth processing when mapping or expanding attributes (e.g. %{string:...} or attribute references) and value-pairs.
Root Cause
rad_vp2data()(insrc/lib/radius.c) uses a per-thread buffer to convert VALUE_PAIR data to network byte order. It allocates withmalloc()and stores the pointer in thread-local storage viafr_thread_local_setup/fr_thread_local_init/fr_thread_local_set.- The buffer is intended to be freed when the thread exits via a pthread key destructor registered in
src/include/threads.h. - When using compiler TLS (
__THREAD/TLS_STORAGE_CLASS, e.g. on macOS with clang), only the first thread that ran the one-time key init had ever calledpthread_setspecific(). All other worker threads never registered their buffer with the key. - When a thread exits, the destructor is only run for that thread if it had previously called
pthread_setspecificfor that key. Worker threads that never did so had their buffers never freed → leak.
Potential Fix (threads.h __THREAD branch)
File: src/include/threads.h
- Removed
pthread_setspecificfrom the one-time key init, so init only creates the key. - Added a setter
__fr_thread_local_set_##_nthat:- Assigns the new value to the thread-local variable.
- Ensures the key exists via
pthread_once. - If the new value is non-NULL, calls
pthread_setspecific(key, &_n)so this thread’s pointer is registered.
- Wired
fr_thread_local_setto use this setter in the__THREADbranch.
Now, when a worker thread first allocates its buffer in rad_vp2data and calls fr_thread_local_set(rad_vp2data_buff, buffer), that thread registers its buffer with the pthread key; when the thread exits, the destructor runs and frees the buffer.
Log output from the FreeRADIUS daemon
N/ARelevant log output from client utilities
No response