You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by an...@apache.org on 2019/01/30 15:48:20 UTC
[mynewt-core] branch master updated: kernel/os: Fix possible race
in os_mutex_release()
This is an automated email from the ASF dual-hosted git repository.
andk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git
The following commit(s) were added to refs/heads/master by this push:
new f4182fe kernel/os: Fix possible race in os_mutex_release()
f4182fe is described below
commit f4182fe3557f16e2a2963974add370f0e1c97afb
Author: Andrzej Kaczmarek <an...@codecoup.pl>
AuthorDate: Wed Jan 30 14:55:03 2019 +0100
kernel/os: Fix possible race in os_mutex_release()
There is possible race between os_mutex_release() and os_mutex_pend()
due to modification of mu_level outside critical section. The scenario
is as follows:
1. os_mutex_release() decrements mu_level on mutex which is not nested
this means that now mu_level==0 and will be unlocked
2. current task is preempted by another one which calls os_mutex_pend()
on the same mutex
3. os_mutex_pend() checks if mu_level==0 (i.e. unlocked) and locks it
by setting mu_level=1 and mu_owner=<task>
4. previous task resumes execution and enters critical section which
should unlock the mutex and overwrites mu_owner=NULL (assuming there
is no other task waiting to lock this mutex)
5. the result is that mutex has mu_level>0 so it is assumed locked, but
at the same time mu_owner==NULL which will cause hardfault when
trying to lock it again due to NULL pointer access when comparing
task priorities
The solution here is to modify os_mutex_release() to check if mutex is
nested with mu_level==1 (i.e. last lock) and if it is, remove last
reference inside critical section.
---
kernel/os/src/os_mutex.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/kernel/os/src/os_mutex.c b/kernel/os/src/os_mutex.c
index 5d1f880..3044e95 100644
--- a/kernel/os/src/os_mutex.c
+++ b/kernel/os/src/os_mutex.c
@@ -79,15 +79,18 @@ os_mutex_release(struct os_mutex *mu)
goto done;
}
- /* Decrement nesting level by 1. If not zero, nested (so dont release!) */
- --mu->mu_level;
- if (mu->mu_level != 0) {
+ /* Don't release if nested, just decrement nesting level */
+ if (mu->mu_level != 1) {
+ --mu->mu_level;
ret = OS_OK;
goto done;
}
OS_ENTER_CRITICAL(sr);
+ /* Decrement nesting level (this effectively sets nesting level to 0) */
+ --mu->mu_level;
+
/* Restore owner task's priority; resort list if different */
if (current->t_prio != mu->mu_prio) {
current->t_prio = mu->mu_prio;