Changeset 07d4271 in mainline for kernel/generic/src/proc/task.c


Ignore:
Timestamp:
2024-01-25T16:22:55Z (12 months ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master
Children:
f8b69a1e
Parents:
1a1e124
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2024-01-25 15:56:31)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2024-01-25 16:22:55)
Message:

Fix some unsound task reference manipulation and locking

In some operations that take task ID as an argument,
there's a possibility of the task being destroyed mid-operation
and a subsequent use-after-free situation.
As a general solution, task_find_by_id() is reimplemented to
check for this situation and always return a valid strong reference.
The callers then only need to handle the reference itself, and
don't need to concern themselves with tasks_lock.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/proc/task.c

    r1a1e124 r07d4271  
    158158                return rc;
    159159
    160         atomic_store(&task->refcount, 0);
    161160        atomic_store(&task->lifecount, 0);
    162161
     
    201200        if (!task)
    202201                return NULL;
     202
     203        refcount_init(&task->refcount);
    203204
    204205        task_create_arch(task);
     
    268269 *
    269270 */
    270 void task_destroy(task_t *task)
     271static void task_destroy(task_t *task)
    271272{
    272273        /*
     
    299300void task_hold(task_t *task)
    300301{
    301         atomic_inc(&task->refcount);
     302        refcount_up(&task->refcount);
    302303}
    303304
     
    311312void task_release(task_t *task)
    312313{
    313         if ((atomic_predec(&task->refcount)) == 0)
     314        if (refcount_down(&task->refcount))
    314315                task_destroy(task);
    315316}
     
    416417/** Find task structure corresponding to task ID.
    417418 *
    418  * The tasks_lock must be already held by the caller of this function and
    419  * interrupts must be disabled.
    420  *
    421419 * @param id Task ID.
    422420 *
    423  * @return Task structure address or NULL if there is no such task ID.
     421 * @return Task reference or NULL if there is no such task ID.
    424422 *
    425423 */
    426424task_t *task_find_by_id(task_id_t id)
    427425{
    428         assert(interrupts_disabled());
    429         assert(irq_spinlock_locked(&tasks_lock));
     426        task_t *task = NULL;
     427
     428        irq_spinlock_lock(&tasks_lock, true);
    430429
    431430        odlink_t *odlink = odict_find_eq(&tasks, &id, NULL);
    432         if (odlink != NULL)
    433                 return odict_get_instance(odlink, task_t, ltasks);
    434 
    435         return NULL;
     431        if (odlink != NULL) {
     432                task = odict_get_instance(odlink, task_t, ltasks);
     433
     434                /*
     435                 * The directory of tasks can't hold a reference, since that would
     436                 * prevent task from ever being destroyed. That means we have to
     437                 * check for the case where the task is already being destroyed, but
     438                 * not yet removed from the directory.
     439                 */
     440                if (!refcount_try_up(&task->refcount))
     441                        task = NULL;
     442        }
     443
     444        irq_spinlock_unlock(&tasks_lock, true);
     445
     446        return task;
    436447}
    437448
     
    524535static void task_kill_internal(task_t *task)
    525536{
    526         irq_spinlock_lock(&task->lock, false);
     537        irq_spinlock_lock(&task->lock, true);
    527538
    528539        /*
     
    534545        }
    535546
    536         irq_spinlock_unlock(&task->lock, false);
     547        irq_spinlock_unlock(&task->lock, true);
    537548}
    538549
     
    552563                return EPERM;
    553564
    554         irq_spinlock_lock(&tasks_lock, true);
    555 
    556565        task_t *task = task_find_by_id(id);
    557         if (!task) {
    558                 irq_spinlock_unlock(&tasks_lock, true);
     566        if (!task)
    559567                return ENOENT;
    560         }
    561568
    562569        task_kill_internal(task);
    563         irq_spinlock_unlock(&tasks_lock, true);
    564 
     570        task_release(task);
    565571        return EOK;
    566572}
     
    592598        }
    593599
    594         irq_spinlock_lock(&tasks_lock, true);
    595600        task_kill_internal(TASK);
    596         irq_spinlock_unlock(&tasks_lock, true);
    597 
    598601        thread_exit();
    599602}
     
    624627        if (additional)
    625628                printf("%-8" PRIu64 " %9zu", task->taskid,
    626                     atomic_load(&task->refcount));
     629                    atomic_load(&task->lifecount));
    627630        else
    628631                printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %10p %10p"
     
    636639                printf("%-8" PRIu64 " %9" PRIu64 "%c %9" PRIu64 "%c "
    637640                    "%9zu\n", task->taskid, ucycles, usuffix, kcycles,
    638                     ksuffix, atomic_load(&task->refcount));
     641                    ksuffix, atomic_load(&task->lifecount));
    639642        else
    640643                printf("%-8" PRIu64 " %-14s %-5" PRIu32 " %18p %18p\n",
Note: See TracChangeset for help on using the changeset viewer.