-
Notifications
You must be signed in to change notification settings - Fork 297
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
dlt-user: Fix crashes in dlt_free during dlt_init #362
Conversation
src/lib/dlt_user.c
Outdated
static DltUser dlt_user; | ||
static atomic_bool dlt_user_initialised = false; | ||
static enum InitState dlt_user_init_state = INIT_UNITIALIZED; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be "static volatile enum ..." as you're later using atomic_compare_exchange_strong with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes good point, added it 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be:
static _Atomic enum InitState dlt_user_init_state = INIT_UNITIALIZED;
and also:
static _Atomic int dlt_user_freeing = 0;
otherwise it does not compile with clang anymore.
2be5223
to
c608cc7
Compare
return DLT_RETURN_OK; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @alexmohr ,
i think we still have a race condition here:
- Thead 1: dlt_init() is called
- Thread 2: dlt_init() is called, immediately returns DLT_OK as atomic operation fails at this point here
- Thread 1: continues initialization and fails due to whatever reason.
so for thread 2 everything would have been fine but DLT library is still not initialzed.
Would a mutex make sense here to block thread 2 until thread 1 has finished the initialization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @michael-methner the described scenario could leave the library in an initialized state.
Adding a mutex here sounds like a good idea. I'll add it later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michael-methner It's done in case you did not notice it yet :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @alexmohr ,
sorry for the delay and thank you very much for the rework. I was quite busy the last weeks. I need some time to carefully review this as it is related to race conditions ;). I hope I can manage before end of this week.
One first question: Would it make sense to use the same mutex in dlt_free() as well to avoid race conditions between dlt_init() and dlt_free().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's necessary as the atomic variable already should make sure that the free method is not running in parallel. In contrast to init, multiple tries of free do not make that much sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @alexmohr ,
thanks for your comment. I agree with your statement that the mutex is not required in dlt_free(). It should be fine as the state is checked with the macro DLT_USER_INITALIZED().
I think the PR good to merge after the typos are fixed and the two static variables are set to "_Atomic".
And sorry, that you had to wait that long for the feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the feedback. Both variables are changed to _Atomic
and it is building locally with clang 10 and gcc 10.3
b8b0992
to
bcd9d39
Compare
@@ -90,8 +90,17 @@ | |||
# define DLT_LOG_FATAL_RESET_TRAP(LOGLEVEL) | |||
#endif /* DLT_FATAL_LOG_RESET_ENABLE */ | |||
|
|||
enum InitState { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to have if the name of enum should be DltInitState
src/lib/dlt_user.c
Outdated
enum InitState expectedInitState = INIT_UNITIALIZED; | ||
if (!(atomic_compare_exchange_strong(&dlt_user_init_state, &expectedInitState, INIT_IN_PROGRESS))) { | ||
/* skip init if we're initialized, otherwise try init (again) */ | ||
if (dlt_user_init_state == INIT_DONE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to have: the condition should be "if (DLT_USER_INITALIZED)"
@@ -1735,7 +1762,7 @@ int dlt_set_resend_timeout_atexit(uint32_t timeout_in_milliseconds) | |||
if (g_dlt_is_child) | |||
return DLT_RETURN_ERROR; | |||
|
|||
if (dlt_user_initialised == 0) | |||
if (DLT_USER_INITALIZED == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to have: the condition should be "if (!DLT_USER_INITALIZED)"
src/lib/dlt_user.c
Outdated
} | ||
|
||
/* prepare for fork() call */ | ||
pthread_atfork(NULL, NULL, &dlt_fork_child_fork_handler); | ||
|
||
/* set success before jump label because in case we used the jump INIT_DON |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice to have: "INIT_DONE" instead "INIT_DON"
@lvklevankhanh thanks for your comments I'm going to fix as soon as I'm back home. It will take a couple of weeks though. Just saying so you don't close the PR due to inactivity :) |
@@ -90,8 +90,17 @@ | |||
# define DLT_LOG_FATAL_RESET_TRAP(LOGLEVEL) | |||
#endif /* DLT_FATAL_LOG_RESET_ENABLE */ | |||
|
|||
enum InitState { | |||
INIT_UNITIALIZED, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: INIT_UNINTIALIZED
static atomic_bool dlt_user_initialised = false; | ||
static volatile enum InitState dlt_user_init_state = INIT_UNITIALIZED; | ||
static pthread_mutex_t dlt_user_init_mtx = PTHREAD_MUTEX_INITIALIZER; | ||
#define DLT_USER_INITALIZED (dlt_user_init_state == INIT_DONE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: DLT_USER_INITIALIZED
If dlt_free is called while dlt_init is running, the dlt-user library crashes because the init and free where not thread safe. This commit introduces a new field dlt_user_initialising which will prevent entering dlt_free while dlt_init is running. Correctness is ensured by a unit test Signed-off-by: Alexander Mohr <[email protected]>
bcd9d39
to
203d788
Compare
If dlt_free is called while dlt_init is running, the dlt-user library
crashes because the init and free where not thread safe.
This commit introduces a new field dlt_user_initialising
which will prevent entering dlt_free while dlt_init is running.
Correctness is ensured by a unit test
The program was tested solely for our own use cases, which might differ from yours.
Licensed under Mozilla Public License Version 2.0
Alexander Mohr, [email protected], Mercedes-Benz Tech Innovation GmbH, imprint