[kernel/mutex] fix: reject deleted mutex waiters - #11730
Conversation
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: kernelReviewers: @GorrayLi @ReviewSun @hamburger-os @lianux-mm @wdfk-prog @xu18838022837 Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-08-28 18:27 CST)
📝 Review Instructions
|
|
|
11e636e to
cbde064
Compare
都改了, 也增加了新的 测试, 新版本 可以再review下 |
这里有个结构上的优化建议。 目前
这样在 我觉得可以考虑把 waiter detach 和 PI recompute 拆成两层,例如: /* 只负责 waiter 与 mutex 脱钩,不处理 PI */
static rt_mutex_t _mutex_detach_waiter_locked(rt_thread_t thread,
rt_bool_t remove_from_list);
/* timeout 路径:解绑 waiter 后完成必要的 PI 更新 */
rt_bool_t rt_mutex_timeout_waiter(rt_thread_t thread);这样三个路径的职责可以更清晰: 感觉这样不仅多 waiter 场景下开销更小,接口职责也会更清晰: 可以考虑一下这种拆法。 |
|
补充考虑了一下,这个问题可能不完全是 mutex 特有的问题。 当前 PR 修复的直接问题确实是 mutex waiter 在被唤醒或 timeout 后,还会继续访问 mutex 来做 priority inheritance、wait-list cleanup 和 不过从更底层看,这里还涉及一个通用问题: timeout callback 和 IPC 唤醒方之间存在 wakeup ownership 竞争。 例如:
mutex 因为还有 PI、owner priority、
目前看到几个值得关注的点。 1.
|
3c34f48 to
29e39b8
Compare
|
感谢建议,我后续再修改下 |
9961da0 to
9169122
Compare
|
@wdfk-prog
本地验证:
最新版本已经更新到这个 PR,麻烦有空再帮忙 review 一下,谢谢。 |
Mutex waiters can race with timeout callbacks, mutex release, object deletions, and thread exit. A waiter may already be READY while its take path has not resumed, so clearing or reusing the mutex object at the wrong time can lead to stale accesses, incorrect error values, or corrupted priority-inheritance state. - Add rt_mutex_timeout_waiter() to detach a timed-out waiter while the scheduler is locked. Update the mutex priority and the owner priority before the waiter is inserted into the ready queue. - Use rt_sched_thread_ready() to arbitrate timeout, release, and delete wakeups. This prevents two paths from claiming the same waiter and lets mutex release skip a waiter whose timeout callback owns the wakeup. - Keep thread->pending_object pointing to the mutex after release hands ownership to a waiter. The pointer is used as an in-flight handoff token until _rt_mutex_take() completes its wakeup handling. - Consume the handoff token under the scheduler lock before touching the mutex. Clear the token only after the handoff is validated, and return RT_EINTR for an unexpected resume that did not grant ownership. - Clear the handoff token and set RT_ERROR before deleting or detaching the mutex. This prevents the resumed waiter from dereferencing an object whose storage has already been released or reused. - Handle the handoff token during thread exit so a handed-off owner is not incorrectly removed from the mutex wait list. - Add regression tests covering dynamic and static mutex deletion, timeout-to-READY races, timeout and release races, timeout-owned release heads, cross-thread owner cleanup, and post-handoff deletion. The mutex structure and ABI remain unchanged. The internal timeout waiter interface is exposed only to kernel and IPC sources. Verified on QEMU with core.mutex using SMP with 2 CPUs, UP with 1 CPU, and SMP with 1 CPU with RT_DEBUGING_ASSERT and RT_DEBUGING_CRITICAL enabled. Signed-off-by: Hui Su <3164683437@qq.com>
9169122 to
98a9deb
Compare
A mutex waiter can race with timeout callbacks, mutex release, object
deletion/detachment, and thread exit. A waiter may already be READY while
its
rt_mutex_take()path has not resumed yet, so accessing the mutex afterrt_schedule()without a lifetime guarantee can result in stale accesses,incorrect wakeup errors, or corrupted priority-inheritance state.
The problematic cases include:
rt_mutex_delete()/rt_mutex_detach()waking a suspended waiter andreleasing or reusing the mutex before the waiter resumes.
state, followed by the mutex being deleted before the waiter runs.
delete/detachorrt_mutex_release()racing with a timeout callback thatalready owns the waiter's timer wakeup.
waiter is still READY and has not returned from
rt_schedule().were still a normal mutex wait dependency.
The fix makes the waiter lifetime and wakeup ownership explicit.
Add
_mutex_detach_waiter_locked()to unlink a mutex waiter and clear itspending_objectwhile the scheduler lock is held. This helper only performswaiter bookkeeping and does not recompute PI state.
Add
rt_mutex_timeout_waiter()for the mutex timeout path. It detaches thewaiter, updates the mutex priority, and updates the owner priority before the
timed-out thread is inserted into the ready queue.
Use
rt_sched_thread_ready()in delete/detach and release paths to arbitratewakeup ownership between the IPC path and an in-flight timeout callback.
If
rt_sched_thread_ready()succeeds, the IPC path owns the wakeup.If it fails because the timeout path already owns the timer wakeup, only the
mutex-specific waiter state is detached and the timeout callback remains
responsible for making the thread READY and setting its timeout error.
Batch PI recomputation in delete/detach. Waiters are detached without
repeatedly recalculating the old owner's inherited priority; the owner PI
state is recomputed after the mutex is removed from the owner's taken list.
In
rt_mutex_release(), skip timeout-owned head waiters and continue lookingfor the next valid waiter instead of treating the first failed wakeup as if
there were no usable waiters.
Keep
thread->pending_objectpointing to the mutex after a successful releasehandoff. While the new owner has not completed its
rt_mutex_take()wakeuppath, this pointer acts as an in-flight handoff token and provides the mutex
lifetime guarantee required by the resumed waiter.
Delete/detach clears this handoff token and sets
RT_ERRORbefore the mutexobject can become invalid.
After
rt_schedule()returns,_rt_mutex_take()only dereferences the mutexwhen the handoff token is still valid. If another wakeup/delete path has
already cleared the token, it returns the corresponding error without
accessing the mutex again.
Thread exit recognizes
pending_object == mutex && mutex->owner == threadasan in-flight handoff token rather than a normal mutex wait dependency.
The mutex structure and ABI are unchanged. The internal timeout/drop helpers
are exposed only to kernel/IPC source files.
Regression coverage includes:
waiter;
waiter resumes;
memory poisoning before the waiter resumes;
Validation:
core.mutexregression tests pass.RT_DEBUGING_ASSERTandRT_DEBUGING_CRITICALwere enabled duringvalidation.
scons -j$(nproc) --strict -C bsp/qemu-vexpress-a9passes.为什么提交这份 PR (why to submit this PR)
修复 mutex waiter 在 timeout、release handoff、delete/detach 和 thread exit
并发情况下可能在 mutex 生命周期结束后继续访问 mutex 的问题。
核心原则是:waiter 从
rt_schedule()返回以后,只有仍持有有效的 mutexhandoff token 时才允许再次访问 mutex;如果 timeout、delete 或其他 wakeup
路径已经完成 cleanup,则直接返回对应错误,不再解引用可能已经失效的 mutex。
你的解决方案是什么 (what is your solution)
将 mutex waiter unlink 与 PI recompute 分离:
_mutex_detach_waiter_locked()仅维护 waiter 生命周期,rt_mutex_timeout_waiter()和各调用路径负责必要的 priority inheritance 更新。timeout waiter 在进入 READY 前完成 mutex-specific cleanup。
delete/detach 和 release 使用
rt_sched_thread_ready()仲裁 wakeup ownership,避免 timeout callback 与 IPC 路径同时处理同一个 waiter。
delete/detach 批量清理 waiter 后只重新计算一次 owner PI。
release 跳过 timeout-owned 的队首 waiter并继续寻找下一个有效 waiter。
release 成功 handoff 后保留
pending_object作为 in-flight lifetime token,直到
_rt_mutex_take()完成 handoff 校验。delete/detach 在 mutex 失效前清除此 token,并使 resumed waiter 返回
-RT_ERROR。_rt_mutex_take()在 token 已失效时直接返回错误,不再通过旧 fallback路径重新访问 mutex。
thread exit 区分普通 mutex waiter 和 handed-off mutex owner。
请提供验证的 bsp 和 config (provide the config and bsp)
BSP:
bsp/qemu-vexpress-a9Config:
RT_USING_MUTEXRT_USING_HEAPfor dynamic mutex regression casesRT_DEBUGING_ASSERTandRT_DEBUGING_CRITICALValidation:
core.mutexpassedcore.mutexpassedcore.mutexpassed当前拉取/合并请求的状态 Intent for your PR
必须选择一项 Choose one (Mandatory):
代码质量 Code Quality:
我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:
#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up