Skip to content

Commit 11e636e

Browse files
committed
[kernel/mutex] fix: reject deleted mutex waiters
A waiter resumed by rt_mutex_delete() or rt_mutex_detach() could continue accessing the deleted mutex through pending_object. This could cause a data abort or use-after-free when the object was freed or reused. Clear pending_object for all blocked waiters before waking them. Return -RT_ERROR after a deleted mutex waiter resumes instead of dereferencing the stale object. Reproduction with the regression test applied to the parent version: - UP QEMU reused the deleted mutex address and filled it with 0xA5. - The core.mutex/test_mutex_delete_waiter test then reported a data abort with pc=0x6007a7bc and r00/r03=0xa5a5a5a5. - The symbolized stack was: rt_sched_thread_get_curr_prio() [src/scheduler_comm.c:125] _rt_mutex_take() [src/ipc.c:1489] rt_mutex_take() [src/ipc.c:1542] mutex_waiter_entry() [src/utest/mutex_tc.c:919] Add regression coverage for dynamic mutex deletion and static mutex detachment with object memory reuse. Validation: - Parent UP QEMU: core.mutex/test_mutex_delete_waiter triggered a data abort. - Fixed UP QEMU: core.mutex passed, including test_mutex_delete_waiter. - scons -j$(nproc) --strict -C bsp/qemu-vexpress-a9 passed. Signed-off-by: Hui Su <3164683437@qq.com>
1 parent 6ea6827 commit 11e636e

2 files changed

Lines changed: 158 additions & 0 deletions

File tree

src/ipc.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,8 +949,21 @@ static void _mutex_before_delete_detach(rt_mutex_t mutex)
949949
{
950950
rt_sched_lock_level_t slvl;
951951
rt_bool_t need_schedule = RT_FALSE;
952+
rt_list_t *node;
952953

953954
rt_spin_lock(&(mutex->spinlock));
955+
956+
/* Clear pending objects before deleting the mutex. */
957+
rt_sched_lock(&slvl);
958+
for (node = mutex->parent.suspend_thread.next;
959+
node != &(mutex->parent.suspend_thread);
960+
node = node->next)
961+
{
962+
rt_thread_t thread = RT_THREAD_LIST_NODE_ENTRY(node);
963+
thread->pending_object = RT_NULL;
964+
}
965+
rt_sched_unlock(slvl);
966+
954967
/* wakeup all suspended threads */
955968
rt_susp_list_resume_all(&(mutex->parent.suspend_thread), RT_ERROR);
956969

@@ -1452,6 +1465,13 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend
14521465
/* do schedule */
14531466
rt_schedule();
14541467

1468+
/* Return the deletion error after the mutex was removed. */
1469+
if (thread->pending_object == RT_NULL &&
1470+
thread->error == RT_ERROR)
1471+
{
1472+
return -RT_ERROR;
1473+
}
1474+
14551475
rt_spin_lock(&(mutex->spinlock));
14561476

14571477
if (mutex->owner == thread)

src/utest/mutex_tc.c

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
#define __RT_IPC_SOURCE__
4545

4646
#include <rtthread.h>
47+
#include <rtsched.h>
4748
#include <stdlib.h>
4849
#include "utest.h"
4950

@@ -904,10 +905,140 @@ static void test_cross_thread_delete_mutex_owner(void)
904905
}
905906
#endif /* RT_USING_HEAP */
906907

908+
#ifdef RT_USING_HEAP
909+
static struct rt_semaphore mutex_delete_ready;
910+
static struct rt_semaphore mutex_delete_done;
911+
static rt_mutex_t mutex_delete_dynamic;
912+
static void *mutex_delete_replacement_memory;
913+
static struct rt_mutex mutex_delete_static;
914+
static volatile rt_err_t mutex_delete_wait_result;
915+
static rt_thread_t mutex_delete_waiter;
916+
static rt_bool_t mutex_delete_semaphores_initialized;
917+
918+
/* Record the result returned by a waiter after its mutex is deleted. */
919+
static void mutex_delete_waiter_entry(void *parameter)
920+
{
921+
rt_sem_release(&mutex_delete_ready);
922+
mutex_delete_wait_result =
923+
rt_mutex_take((rt_mutex_t)parameter, RT_WAITING_FOREVER);
924+
rt_sem_release(&mutex_delete_done);
925+
}
926+
927+
/* Wait until the mutex waiter enters the suspend list. */
928+
static void mutex_delete_wait_until_suspended(rt_thread_t thread)
929+
{
930+
rt_sched_lock_level_t slvl;
931+
932+
for (;;)
933+
{
934+
rt_sched_lock(&slvl);
935+
if ((RT_SCHED_CTX(thread).stat & RT_THREAD_SUSPEND_MASK) ==
936+
RT_THREAD_SUSPEND_MASK)
937+
{
938+
rt_sched_unlock(slvl);
939+
break;
940+
}
941+
rt_sched_unlock(slvl);
942+
rt_thread_delay(1);
943+
}
944+
}
945+
946+
/* Verify that deleted mutex waiters do not access stale objects. */
947+
static void test_mutex_delete_waiter(void)
948+
{
949+
mutex_delete_dynamic = rt_mutex_create("delmtx", RT_IPC_FLAG_PRIO);
950+
uassert_true(mutex_delete_dynamic != RT_NULL);
951+
uassert_int_equal(rt_mutex_take(mutex_delete_dynamic,
952+
RT_WAITING_FOREVER),
953+
RT_EOK);
954+
955+
uassert_int_equal(rt_sem_init(&mutex_delete_ready,
956+
"mtxready",
957+
0,
958+
RT_IPC_FLAG_FIFO),
959+
RT_EOK);
960+
uassert_int_equal(rt_sem_init(&mutex_delete_done,
961+
"mtxdone",
962+
0,
963+
RT_IPC_FLAG_FIFO),
964+
RT_EOK);
965+
mutex_delete_semaphores_initialized = RT_TRUE;
966+
mutex_delete_wait_result = -RT_ERROR;
967+
968+
mutex_delete_waiter = rt_thread_create("mtxwait",
969+
mutex_delete_waiter_entry,
970+
mutex_delete_dynamic,
971+
UTEST_THR_STACK_SIZE,
972+
UTEST_THR_PRIORITY + 1,
973+
10);
974+
uassert_true(mutex_delete_waiter != RT_NULL);
975+
rt_thread_startup(mutex_delete_waiter);
976+
977+
uassert_int_equal(rt_sem_take(&mutex_delete_ready,
978+
RT_WAITING_FOREVER),
979+
RT_EOK);
980+
mutex_delete_wait_until_suspended(mutex_delete_waiter);
981+
982+
uassert_int_equal(rt_mutex_delete(mutex_delete_dynamic), RT_EOK);
983+
mutex_delete_dynamic = RT_NULL;
984+
985+
mutex_delete_replacement_memory =
986+
rt_malloc(sizeof(struct rt_mutex));
987+
uassert_true(mutex_delete_replacement_memory != RT_NULL);
988+
if (mutex_delete_replacement_memory ==
989+
(void *)mutex_delete_waiter->parameter)
990+
{
991+
rt_memset(mutex_delete_replacement_memory,
992+
0xA5,
993+
sizeof(struct rt_mutex));
994+
}
995+
996+
uassert_int_equal(rt_sem_take(&mutex_delete_done,
997+
rt_tick_from_millisecond(1000)),
998+
RT_EOK);
999+
uassert_int_equal(mutex_delete_wait_result, -RT_ERROR);
1000+
rt_free(mutex_delete_replacement_memory);
1001+
mutex_delete_replacement_memory = RT_NULL;
1002+
1003+
uassert_int_equal(rt_mutex_init(&mutex_delete_static,
1004+
"detmtx",
1005+
RT_IPC_FLAG_PRIO),
1006+
RT_EOK);
1007+
uassert_int_equal(rt_mutex_take(&mutex_delete_static,
1008+
RT_WAITING_FOREVER),
1009+
RT_EOK);
1010+
1011+
mutex_delete_waiter = rt_thread_create("mtxwait2",
1012+
mutex_delete_waiter_entry,
1013+
&mutex_delete_static,
1014+
UTEST_THR_STACK_SIZE,
1015+
UTEST_THR_PRIORITY + 1,
1016+
10);
1017+
uassert_true(mutex_delete_waiter != RT_NULL);
1018+
rt_thread_startup(mutex_delete_waiter);
1019+
1020+
uassert_int_equal(rt_sem_take(&mutex_delete_ready,
1021+
RT_WAITING_FOREVER),
1022+
RT_EOK);
1023+
mutex_delete_wait_until_suspended(mutex_delete_waiter);
1024+
1025+
uassert_int_equal(rt_mutex_detach(&mutex_delete_static), RT_EOK);
1026+
rt_memset(&mutex_delete_static, 0xA5, sizeof(mutex_delete_static));
1027+
1028+
uassert_int_equal(rt_sem_take(&mutex_delete_done,
1029+
rt_tick_from_millisecond(1000)),
1030+
RT_EOK);
1031+
uassert_int_equal(mutex_delete_wait_result, -RT_ERROR);
1032+
}
1033+
#endif /* RT_USING_HEAP */
1034+
9071035
static rt_err_t utest_tc_init(void)
9081036
{
9091037
#ifdef RT_USING_HEAP
9101038
dynamic_mutex = RT_NULL;
1039+
mutex_delete_dynamic = RT_NULL;
1040+
mutex_delete_replacement_memory = RT_NULL;
1041+
mutex_delete_semaphores_initialized = RT_FALSE;
9111042
#endif /* RT_USING_HEAP */
9121043

9131044
return RT_EOK;
@@ -916,6 +1047,12 @@ static rt_err_t utest_tc_init(void)
9161047
static rt_err_t utest_tc_cleanup(void)
9171048
{
9181049
#ifdef RT_USING_HEAP
1050+
if (mutex_delete_semaphores_initialized)
1051+
{
1052+
rt_sem_detach(&mutex_delete_ready);
1053+
rt_sem_detach(&mutex_delete_done);
1054+
mutex_delete_semaphores_initialized = RT_FALSE;
1055+
}
9191056
dynamic_mutex = RT_NULL;
9201057
#endif /* RT_USING_HEAP */
9211058

@@ -936,6 +1073,7 @@ static void testcase(void)
9361073
UTEST_UNIT_RUN(test_dynamic_mutex_trytake);
9371074
UTEST_UNIT_RUN(test_dynamic_pri_reverse);
9381075
UTEST_UNIT_RUN(test_cross_thread_delete_mutex_owner);
1076+
UTEST_UNIT_RUN(test_mutex_delete_waiter);
9391077
#endif
9401078
UTEST_UNIT_RUN(test_recurse_lock);
9411079
}

0 commit comments

Comments
 (0)