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

dlt-user: Fix crashes in dlt_free during dlt_init #362

Merged
merged 1 commit into from
Jan 11, 2023

Conversation

alexmohr
Copy link
Contributor

@alexmohr alexmohr commented Jan 19, 2022

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

static DltUser dlt_user;
static atomic_bool dlt_user_initialised = false;
static enum InitState dlt_user_init_state = INIT_UNITIALIZED;
Copy link

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?

Copy link
Contributor Author

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 👍

Copy link
Collaborator

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.

return DLT_RETURN_OK;
}
Copy link
Collaborator

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:

  1. Thead 1: dlt_init() is called
  2. Thread 2: dlt_init() is called, immediately returns DLT_OK as atomic operation fails at this point here
  3. 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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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 :)

Copy link
Collaborator

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().

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

Copy link
Contributor Author

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

@alexmohr alexmohr force-pushed the fix_init_free_error branch 2 times, most recently from b8b0992 to bcd9d39 Compare November 10, 2022 11:55
@@ -90,8 +90,17 @@
# define DLT_LOG_FATAL_RESET_TRAP(LOGLEVEL)
#endif /* DLT_FATAL_LOG_RESET_ENABLE */

enum InitState {
Copy link
Contributor

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

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)
Copy link
Contributor

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)
Copy link
Contributor

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)"

}

/* 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
Copy link
Contributor

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"

@alexmohr
Copy link
Contributor Author

alexmohr commented Dec 3, 2022

@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,
Copy link
Collaborator

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)
Copy link
Collaborator

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]>
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

Successfully merging this pull request may close these issues.

4 participants