[Prior 4] Fix resource leaks, signal handling, and clean lifecycle management in dlt-qnx-system - #917
santhoshsivanhere wants to merge 4 commits into
Conversation
|
@minminlittleshrimp Please spend some time to review this PR to fix issue #890. |
The slogger2 adapter previously suffered from unhandled json_decoder leaks, potential division-by-zero during buffer checks, and unmanaged DltContext pointers that caused dangling references during teardown. Address these stability and resource management issues: - Wrap json_decoder_t with an RAII deleter to guarantee cleanup on all exit paths in dlt_context_map_read(). - Continue parsing remaining entries when JSON object/key extraction fails instead of breaking early. - Guard against total_size <= 0 in wait_for_buffer_space() to prevent division by zero. - Explicitly unregister all DltContext entries in clean_qnx_slogger2() before tearing down storage. - Replace raw pointers in g_slog2file with std::unique_ptr<DltContext> to enforce exception-safe ownership. Signed-off-by: Santhosh Sivan Murugan <santhoshsivanhere@gmail.com> This PR fixes COVESA#890.
Refactor thread stack allocation in dlt-qnx-slogger2-adapter to use mmap() and munmap() instead of malloc() and free(). Using mmap() with MAP_ANONYMOUS | MAP_PRIVATE guarantees proper page-aligned memory allocation directly from the OS, eliminating the need for manual alignment calculations before calling pthread_attr_setstack(). Changes: - Replaced malloc() call with mmap() using PROT_READ | PROT_WRITE. - Replaced free() in free_stackaddr() with munmap() referencing STACK_ALLOC_SIZE. - Defined STACK_ALLOC_SIZE constant to ensure consistent allocation and cleanup bounds. - Checked against MAP_FAILED for accurate error handling. Signed-off-by: Santhosh Sivan Murugan <santhoshsivanhere@gmail.com> This PR fixes COVESA#890.
- Main Loop & Signals:
- Block SIGINT, SIGTERM, SIGHUP, SIGQUIT, and SIGALRM via
pthread_sigmask prior to thread creation.
- Synchronously wait for termination signals using sigwait() to
avoid async signal handler deadlocks.
- Slogger2 Adapter Lifecycle:
- Add atomic flag `g_slog2_thread_alive` check within
`slogger2_callback` and `wait_for_buffer_space` to break
early on shutdown.
- Ensure custom thread stack memory mapped via mmap is
cleanly unmapped.
- Cleanup Sequence:
- Iterate through `g_slog2file` map to unregister all dynamically
registered DLT contexts upon thread exit.
- Guarantee `pthread_join` completion prior to calling `clean_up()`
and unregistering the main DLT application.
Signed-off-by: Santhosh Sivan Murugan <santhoshsivanhere@gmail.com>
This PR fixes COVESA#890.
f6a6449 to
580b416
Compare
| extern DltQnxSystemThreads g_threads; | ||
|
|
||
| static std::unordered_map<std::string, DltContext*> g_slog2file; | ||
| static std::unordered_map<std::string, std::unique_ptr<DltContext>> g_slog2file; |
There was a problem hiding this comment.
DltContext is a C struct, so it still need dlt native api lifecycle (dlt_register_context/unregis context), we can do like this:
struct DltContextDeleter {
void operator()(DltContext *ctx) const {
if (ctx) {
dlt_unregister_context(ctx);
delete ctx;
}
}
};
using DltContextPtr = std::unique_ptr<DltContext, DltContextDeleter>;
Then:
static std::unordered_map<std::string, DltContextPtr> g_slog2file;
in cleanup simply:
void clean_qnx_slogger2()
{
g_slog2file.clear(); // we ok here
dltWarnedMissingMappings.clear();
free_stackaddr();
}
just RAII same as Jsondecoderdeleter, what do you think?
Thanks
There was a problem hiding this comment.
I dont see dltContextDeleter elsewhere, and DLT_CTX_REG macro still in use
struct DltContextDeleter {
void operator()(DltContext *ctx) const {
if (ctx) {
dlt_unregister_context(ctx);
delete ctx;
}
}
};
| * Wait for threads to exit. | ||
| */ | ||
| static void join_thread() | ||
| /* Point 4: Correct Joining & Context Unregister Sequence */ |
There was a problem hiding this comment.
what it means by point 4? thanks
Implement runtime enable/disable handling for the slog2 adapter. - Add thread-safe runtime enable/disable state management. - Stop, join, clean up, and restart the slog2 thread on injection. - Handle disable/enable race conditions during thread termination. - Add condition-variable based waiting while slog2 is disabled. - Replace slog2 thread stack allocation with mmap and a guard page. - Improve slog2 thread and resource cleanup handling. - Validate slog2 injection service IDs and payloads. - Load slog2 context mappings lazily when the adapter is enabled. - Rename the startup configuration to distinguish it from runtime state. - Improve signal and daemon handling. - Ensure configured DLT IDs are null-terminated. This PR fixes COVESA#890. Signed-off-by: Santhosh Sivan Murugan santhoshsivanhere@gmail.com
| sigaddset(&mask, SIGHUP); | ||
| sigaddset(&mask, SIGQUIT); | ||
| sigaddset(&mask, SIGINT); | ||
| sigaddset(&mask, SIGALRM); |
There was a problem hiding this comment.
Need block also on SIGUSR1?
sigaddset(&mask, SIGUSR1);
Thanks
| bool dlt_slog2_is_disabled(void) | ||
| { | ||
| bool disabled; | ||
| pthread_mutex_lock(&g_threads.lock); |
There was a problem hiding this comment.
Should we add definition for lock and cond in header? Thanks
| return -1; | ||
| } | ||
| dlt_set_main_thread(pthread_self()); | ||
| dlt_slog2_set_disabled(!g_dlt_qnx_conf->qnxslogger2.startupEnabled); |
There was a problem hiding this comment.
where this defined? if it is the enable modified then pls update, thanks
| join_thread(); | ||
|
|
||
| if (ret != 0) { | ||
| if (!dlt_slog2_is_disabled()) { |
There was a problem hiding this comment.
should this define in header? thanks
| * | ||
| * Must be called during shutdown after all threads have been joined. | ||
| */ | ||
| int dlt_threads_destroy(void) |
There was a problem hiding this comment.
yes if this internal func no need to be defined in header, anyway many structs not yet updated?
| */ | ||
| void clean_qnx_slogger2() | ||
| { | ||
| /* RAII Deleter handles dlt_unregister_context() for all map entries automatically */ |
There was a problem hiding this comment.
RAII not completely clean if using macro? should we add:
DLT_UNREGISTER_CONTEXT(dltQnxSlogger2Context);
g_adapter_ctx_registered = false?
Here for cleanup? Thanks
|
|
||
| auto *conf = static_cast<DltQnxSystemConfiguration*>(param); | ||
| DltLogLevelType loglevel; | ||
| switch (info->severity) |
There was a problem hiding this comment.
should we check info null? thanks
| else { | ||
| DLT_LOG(dltQnxSystem, DLT_LOG_ERROR, | ||
| DLT_STRING("Unknown injection data for slog2 adapter:"), | ||
| DLT_STRING((char*) data)); |
There was a problem hiding this comment.
bufferoverflow might happen with this data, kindly check, thanks
| * Must be called once before any other dlt_threads/dlt_set/dlt_get functions. | ||
| * Exits the process on failure. | ||
| */ | ||
| int dlt_threads_init(void) |
There was a problem hiding this comment.
If pthread_condattr_init, pthread_condattr_setclock, or pthread_cond_init fails after pthread_mutex_init succeeds, the function returns without calling pthread_mutex_destroy?
| pthread_condattr_t cond_attr; | ||
| ret = pthread_condattr_init(&cond_attr); | ||
| if (ret != 0) { | ||
| fprintf(stderr, "pthread_condattr_init failed: %s\n", strerror(ret)); |
There was a problem hiding this comment.
pthread_mutex_destroy(&g_threads.lock);?
| * \return char* A pointer to the first non-whitespace character within the | ||
| * original input buffer, or NULL if the input string 's' is NULL. | ||
| */ | ||
| static char *trim_whitespace(char *s) |
There was a problem hiding this comment.
Where this func is used? if not related here can we remove it? also dlt_parser has these such functions, if we need we can used from that common code, thanks
|
|
||
| static void *stackaddr; | ||
| static void *stackaddr = NULL; | ||
| static const size_t STACK_ALLOC_SIZE = PTHREAD_STACK_4K * 4; |
There was a problem hiding this comment.
Using mmap make this deadcode, should we remove it?
| #define STACK_USABLE_SIZE (PTHREAD_STACK_4K * 64u) /* 256 KiB thread stack */ | ||
| #define STACK_TOTAL_SIZE (STACK_GUARD_SIZE + STACK_USABLE_SIZE) | ||
| static std::atomic_bool g_stop_notified{false}; | ||
| static std::atomic_bool g_adapter_ctx_registered{false}; |
There was a problem hiding this comment.
where this bool is checked? if not it a deadcode/bool not-used? thanks
| @@ -460,4 +826,5 @@ static void clean_up() | |||
| free(g_dlt_qnx_conf->qnxslogger2.contextId); | |||
| if (g_dlt_qnx_conf) | |||
| free(g_dlt_qnx_conf); | |||
There was a problem hiding this comment.
is this deadcode? kindly check, thanks (see check line 819 it seems so?)
Summary
This PR hardens the
dlt-qnx-systemslogger2 adapter against resourceleaks, undefined behavior, and unclean shutdowns. It replaces heap-based
thread stack allocation with
mmap, introduces RAII-based ownership forDLT contexts and JSON decoders, and reworks signal handling so the daemon
terminates synchronously and predictably.
Changes
1. Resource leaks and exception safety in slogger2
json_decoder_twith an RAII deleter to guarantee cleanup on allexit paths in
dlt_context_map_read().instead of breaking early.
total_size <= 0inwait_for_buffer_space()to preventdivision by zero.
DltContextentries inclean_qnx_slogger2()before tearing down storage.
g_slog2filewithstd::unique_ptr<DltContext>to enforce exception-safe ownership.
2. Replace malloc-based thread stack allocation with mmap
malloc()/free()for the custom thread stack withmmap()(
MAP_ANONYMOUS | MAP_PRIVATE) andmunmap().STACK_ALLOC_SIZEso allocation and cleanup use identicalbounds.
MAP_FAILEDfor accurate error handling.pthread_attr_setstack().3. Clean lifecycle management and signal handling
SIGINT,SIGTERM,SIGHUP,SIGQUIT, andSIGALRMviapthread_sigmask()before thread creation.sigwait()to avoidasync signal-handler deadlocks.
g_slog2_thread_alive, checked insideslogger2_callback()andwait_for_buffer_space(), to break out earlyon shutdown.
mmap-allocated thread stack is cleanly unmapped.g_slog2fileto unregister all dynamically registeredDLT contexts on thread exit.
pthread_join()completes before callingclean_up()andunregistering the main DLT application.
Motivation
The slogger2 adapter could leak JSON decoders, dereference dangling
DltContextpointers during teardown, divide by zero on empty buffers,and terminate uncleanly on signals. Together these fixes make the
lifecycle of the adapter deterministic and exception-safe.
Testing
dlt-daemonwith-DWITH_DLT_QNX_SYSTEM=ON.on
SIGTERM/SIGINT.Fixes #890.
Signed-off-by: Santhosh Sivan Murugan Murugan.SanthoshSivan@in.bosch.com