代码拉取完成,页面将自动刷新
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
社区讨论:
https://lore.kernel.org/all/20201011075913.GA8065@eldamar.lan/#t
问题场景为 nfs4_set_delegation() 异常流程与 nfsd4_cb_recall_prepare() 流程并发,导致已被释放的 nfs4_delegation 被插入 del_recall_lru 链表后继续访问
修复补丁: 548ec0805c39 "nfsd: fix use-after-free due to delegation race"
// 分配释放 delegation ———— 正常流程
nfs4_open_delegation
// set delegation
nfs4_set_delegation
alloc_init_deleg
delegstateid
nfs4_alloc_stid
kmem_cache_zalloc // 从 deleg_slab 中分配 nfs4_stid
// nfs4_stid 是 nfs4_delegation 的第一个成员,
// 指向nfs4_stid的指针即指向nfs4_delegation的指针
refcount_set(&stid->sc_count, 1) // 初始化 nfs4_stid 计数
hash_delegation_locked // 没有冲突则执行
refcount_inc(&dp->dl_stid.sc_count) // 增加 nfs4_stid 计数
list_add(&dp->dl_perfile, &fp->fi_delegations)
nfs4_put_stid
refcount_dec_and_lock(&s->sc_count, &clp->cl_lock) // 减少 nfs4_stid 计数
// 分配释放 delegation ———— 异常流程
nfs4_open_delegation
nfs4_set_delegation
alloc_init_deleg
delegstateid
nfs4_alloc_stid
kmem_cache_zalloc
refcount_set(&stid->sc_count, 1) // 初始化 nfs4_stid 计数
fp->fi_had_conflict // 有冲突
nfs4_put_stid
refcount_dec_and_lock(&s->sc_count, &clp->cl_lock) // 减少 nfs4_stid 计数
sc_free // 若 nfs4_stid 计数减为0,则释放 nfs4_stid 即 nfs4_delegation
put_deleg_file
--fp->fi_delegees // fp->fi_delegees 减为0
// 分配 delegation 后,释放 delegation 前加入链表 del_recall_lru
// recall
nfsd4_run_cb
queue_work(callback_wq, &cb->cb_work)
nfsd4_run_cb_work
cb->cb_ops->prepare
nfsd4_cb_recall_prepare
// 修复补丁: 在将 delegation 添加到 del_recall_lru 链表前增加 delegation_hashed() 判断
// 如果 dp->dl_perfile 非空,说明在 nfs4_set_delegation() 流程中已经执行了 hash_delegation_locked()
// 进一步说明 nfs4_stid(nfs4_delegation) 没有释放,可以插入链表
list_add_tail(&dp->dl_recall_lru, &nn->del_recall_lru)
异常栈分析:
[7416735.513815] WARNING: CPU: 5 PID: 150353 at fs/nfsd/nfs4state.c:4741 laundromat_main+0x210/0x600 [nfsd]
1) WARNING
// delegation 加入 del_recall_lru 链表(dl_recall_lru),未加入 fi_delegations 链表(dl_perfile)
laundromat_main
nfs4_laundromat
list_for_each_safe(pos, next, &nn->del_recall_lru)
WARN_ON(!unhash_delegation_locked(dp))
// dp->dl_perfile 为空,unhash_delegation_locked 返回 false,触发 WARNING
[7416735.513904] list_del corruption. prev->next should be ffff8df5d2b8d3f0, but was ffffa4d7de533e50
[7416735.513913] WARNING: CPU: 5 PID: 150353 at lib/list_debug.c:53 __list_del_entry_valid+0x79/0x90
2) WARNING
// 待删除的 delegation 的前置结点的后置结点不是该 delegation
// 当前 delegation 已不在原链表中
1. 基于基本问题场景,delegation 被加入 del_recall_lru 的同时,已经被释放
2. 原 delegation 所在内存区域重新分配并初始化
3. 新的 dp->dl_perfile 插入 fi_delegations 链表与原 del_recall_lru 链表中的 delegation 删除并发(新旧两个 delegation 使用同一块内存,但对应不同的文件,无法按预期通过 fp->fi_lock 防止并发)
laundromat_main
nfs4_laundromat
list_for_each_safe(pos, next, &nn->del_recall_lru)
unhash_delegation_locked
list_del_init // &dp->dl_perfile
__list_del_entry
__list_del_entry_valid
[7416735.514198] WARNING: CPU: 5 PID: 150353 at fs/nfsd/nfs4state.c:899 destroy_unhashed_deleg+0x75/0x80 [nfsd]
3) WARNING
laundromat_main
nfs4_laundromat
revoke_delegation
destroy_unhashed_deleg
nfs4_unlock_deleg_lease
WARN_ON_ONCE(!fp->fi_delegees) // fp->fi_delegees 已减为0
[7416735.514257] BUG: unable to handle kernel NULL pointer dereference at 0000000000000028
destroy_unhashed_deleg
4) BUG
laundromat_main
nfs4_laundromat
revoke_delegation
destroy_unhashed_deleg
nfs4_put_stid // dp->dl_stid 作为 delegation 内嵌成员,所在的内存区域已释放(通过kmem_cache_zalloc()重新分配并初始化成0?),解析该地址触发空指针解引用
登录 后才可以发表评论