Skip to content

Unprotected task cancellation when task is holding or waiting for mutext #63

@vicLin8712

Description

@vicLin8712

When the task is waiting or holding a mutex, it can still be removed by another task and never handle the mutex. For example, if the task is holding the mutex and is cancelled by another task, the mutex ownership still the same as this cancelled task.

This issue causes synchronization mechanisms to become invalid.

Test

  • lock_task(): Acquire the mutex and yield to cancel_task(), this task will be cancelled while holding the mutex.
  • cancel_task(): Cancel the task that holds the mutex and try to acquire the mutex.
#include <linmo.h>

static mutex_t m;
int32_t lock_task_id, cancel_task_id;

void lock_task(void)
{
    mo_mutex_lock(&m);
    mo_task_yield();
    mo_mutex_unlock(&m);
    printf("Never return here \n");
    for (;;)
        mo_task_wfi();
}

void cancel_task(void)
{
    mo_task_cancel(lock_task_id);
    printf("Owner ID is %d and lock task ID is %d\n", m.owner_tid, lock_task_id);
    mo_mutex_lock(&m);
    printf("Ownership inconsistent, owner id %d still the same \n", m.owner_tid);
    mo_mutex_unlock(&m);
    for (;;)
        mo_task_wfi();
}

int32_t app_main(void)
{
    mo_logger_flush();
    lock_task_id = mo_task_spawn(lock_task, DEFAULT_STACK_SIZE);
    cancel_task_id = mo_task_spawn(cancel_task, DEFAULT_STACK_SIZE);
    mo_mutex_init(&m);

    /* preemptive mode */
    return 1;
}

Result

$make run 
Ready to launch Linmo kernel + application.
Linmo kernel is starting...
Heap initialized, 130003764 bytes available
task 1: entry=80001210 stack=80004d78 size=1024 prio_level=4 time_slice=5
Logger initialized
task 2: entry=800001d4 stack=80005234 size=8192 prio_level=4 time_slice=5
task 3: entry=8000020c stack=800072bc size=8192 prio_level=4 time_slice=5
Scheduler mode: Preemptive
Owner ID is 2 and lock task ID is 2
Ownership inconsistent, owner id 2

The cancelled task, which is the owner of the mutex, does not reset the mutex properties. As the test results show, the owner ID should be 0 when the mutex holder is cancelled. Or just rejecting cancellation when holding the mutex.

Analysis

Refer to mo_mutex_lock(), the code section below is used for updating new ownership of the mutex; however, the cancelled task while holding the mutex never releases the mutex, which will reset owner_tid = 0.

...
    /* Fast path: mutex is free, acquire immediately */
    if (likely(m->owner_tid == 0)) {
        m->owner_tid = self_tid;
        NOSCHED_LEAVE();
        return ERR_OK;
    }
...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions