翼度科技»论坛 云主机 LINUX 查看内容

频繁设置CGroup触发linux内核bug导致CGroup running task不调度

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
1. 说明1> 本篇是实际工作中linux上碰到的一个问题,一个使用了CGroup的进程处于R状态但不执行,也不退出,还不能kill,经过深入挖掘才发现是Cgroup的内核bug2>发现该bug后,去年给RedHat提交过漏洞,但可惜并未通过,不知道为什么,这里就发我博客公开了3> 前面的2个帖子《极简cfs公平调度算法》《极简组调度-CGroup如何限制cpu》是为了了解本篇这个内核bug而写的,需要linux内核进程调度和CGroup控制的基本原理才能够比较清晰的了解这个内核bug的来龙去脉4> 本文所用的内核调试工具是crash,大家可以到官网上去查看crash命令的使用,这里就不多介绍了https://crash-utility.github.io/help.html 2. 问题2.1 触发bug code(code较长,请展开代码)2.1.1 code
[code]#include #include #include #include #include #include #include #include #include #include #include using namespace std;std::string sub_cgroup_dir("/sys/fs/cgroup/cpu/test");// common libbool is_dir(const std::string& path){    struct stat statbuf;    if (stat(path.c_str(), &statbuf) == 0 )    {        if (0 != S_ISDIR(statbuf.st_mode))        {            return true;        }    }    return false;}bool write_file(const std::string& file_path, int num){    FILE* fp = fopen(file_path.c_str(), "w");    if (fp = NULL)    {        return false;    }    std::string write_data = to_string(num);    fputs(write_data.c_str(), fp);    fclose(fp);    return true;}// mslong get_ms_timestamp(){    timeval tv;    gettimeofday(&tv, NULL);    return (tv.tv_sec * 1000 + tv.tv_usec / 1000);}// cgroupbool create_cgroup(){    if (is_dir(sub_cgroup_dir) == false)    {        if (mkdir(sub_cgroup_dir.c_str(), S_IRWXU | S_IRGRP) != 0)        {            cout timer_active为1,直接return,而不再将period_timer激活
 3.3 搜索__start_cfs_bandwidth()的调用,发现时钟中断中会调用update_curr()函数,其最终会调用assign_cfs_rq_runtime()检查cgroup cpu配额使用情况,决定是否需要throttle,这里在cfs_b->timer_active = 0时,也会调用__start_cfs_bandwidth(),即执行上面B线程的代码,从而和设置CGroup的线程A发生线程竞争,导致timer失效。1> 完整代码执行流程图
 
 
2> 当定时器失效后,由于3.2中线程B将cfs_b->timer_active = 1,所以即使下次时钟中断执行到assign_cfs_rq_runtime()中时,由于误判timer是active的,也不会调用__start_cfs_bandwidth()再次激活timer,这样被throttle的group se永远不会被unthrottle投入rq调度了
 3.4 总结频繁设置CGroup配置,会和时钟中断中检查group quota的线程在__start_cfs_bandwidth()上发生线程竞争,导致period_timer被cancel后不再激活,然后CGroup控制的task不能分配cpu quota,导致不再被调度 3.5 恢复方法知道了漏洞成因,我们也看到tg_set_cfs_quota()会调用__start_cfs_bandwidth() cancel掉timer,然后重新激活timer,这样就能在timer回调中unthrottle了,所以只要手动设置下这个CGroup的cpu.cfs_period_us或cpu.cfs_quota_us,就能恢复运行。 4. 修复3.10.0-693以上的版本并不会出现这个问题,通过和2.6.32版本(下图右边)的代码对比,可知3.10.0-693版的代码(下图左边)将hrtimer_cancel()该为hrtimer_try_to_cancel(),并将其和cfs_b->timer_active的判定都放在自旋锁中保护,这样就不会cfs_b->timer_active被置1后,仍然还会去cancel period_timer的问题了,但看这个bug fix的邮件组讨论,是为了修另一个问题顺便把这个问题也修了,痛失给linux提patch的机会- -<img>
ref : https://gfiber.googlesource.com/kernel/bruno/+/09dc4ab03936df5c5aa711d27c81283c6d09f495 5. 漏洞利用1> 在国内,仍有大量的公司在使用CentOS6和CentOS7.0~7.5,这些系统都存在这个漏洞,使用了CGroup限制cpu就有可能触发这个bug导致业务中断,且还不一定能重启恢复2> 一旦触发这个bug,由于task本身已经是running状态了,即使去kill,由于task得不到调度,是无法kill掉的,因此可以通过这种方法攻击任意软件程序(如杀毒软件),让其不能执行又不能重启(很多程序为了保证不双开,都会只保证只有一个进程存在),即使他们不用CGroup,也可以给他建一个对其进行攻击3> 该bug由于是linux内核bug,一旦触发还不易排查和感知,因为看进程状态都是running,直觉上认为进程仍然在正常执行的
来源:https://www.cnblogs.com/organic/p/17321523.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具