-
Notifications
You must be signed in to change notification settings - Fork 39
Description
Step 0 写到“If the current RCU grace period surpasses RCU_TASK_IPI_DELAY (defaulting to 0.5 seconds), inter-processor interrupts (IPIs) are dispatched to all CPUs to verify that they are not in RCU critical sections”。
这里可能存在错误。
我最近在研究一个类似的use-after-free-by-RCU bug。因此研究了RCU相关代码。
根据我对代码的理解,RCU_TASK_IPI_DELAY和Step 0无关。RCU_TASK_IPI_DELAY只在/kernel/rcu/tasks.h中被赋给rcu_task_ipi_delay,rcu_task_ipi_delay是一个static的int变量,只在同文件的trc_wait_for_one_reader函数中被使用。
见 https://elixir.bootlin.com/linux/v6.1.25/source/kernel/rcu/tasks.h#L143 。
而trc_wait_for_one_reader函数是被带_tasks后缀的RCU同步原语使用的,call_rcu/synchronize_rcu不会使用到它。
真实的情况可能是:
call_rcu/synchronize_rcu等待超过jiffies_till_first_fqs(这是真正决定call_rcu/synchronize_rcu开始quiescent-state forcing的时间参数),调用rcu_gp_fqs,执行force_qs_rnp(rcu_implicit_dynticks_qs),进而执行resched_cpu(rdp->cpu),发送IPI中断,这个IPI中断会设置TIF_NEED_RESCHED。
根据仓库里的内核配置,抢占模式是PREEMPT_DYNAMIC,内核启动时决定,默认设置在PREEMPT_VOLUNTARY。
这样,在IPI中断后,VMA iteration中一旦触发cond_resched,由于设置了TIF_NEED_RESCHED,就会发生调度。进而,报告quiescent-state,call_rcu/synchronize_rcu结束,释放内存,发生UAF。
见 https://elixir.bootlin.com/linux/v6.1.25/source/kernel/rcu/tree.c#L822 。
这种解释能最好的匹配到 Step 0 中描述的现象。
关于 IPIs dispatched to all CPUs to verify that they are not in RCU critical sections:
与之匹配的实现是synchronize_rcu_expedited,相应的IPI检查函数是rcu_exp_handler。但这种情况不会等待,synchronize_rcu_expedited也不会被call_rcu调用,绝大部分情况下也不是synchronize_rcu的默认路径。
见 https://elixir.bootlin.com/linux/v6.1.25/source/kernel/rcu/tree_exp.h#L910 。
关于漏洞利用条件:
综上,我认为StackRot可能还是基于可以发生调度(PREEMPT_VOLUNTARY)才能利用成功,应该不适用于PREEMPT_NONE配置下的内核。
目前最新的kernelctf编译配置已经改成了PREEMPT_NONE加不开启PREEMPT_DYNAMIC,所有use-after-free-by-RCU的bug应该都不能利用了。
我在研究的那个类似的use-after-free-by-RCU bug应该是寄了,不能打kernelctf(