如何处理Linux内核中的常规保护错误 [英] how to handle general protection fault in linux kernel

查看:176
本文介绍了如何处理Linux内核中的常规保护错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个如下的内核模块,它尝试读取msr寄存器,毫无疑问,由于msr 0x2不存在,它将崩溃,将会发生一般的保护错误. 我的问题是,如何像定义GP处理程序那样绕过此错误?如果我看不懂msr,请放手...

I have a piece of kernel module as the following, which tries to read msr register, no doubt it would crash as the msr 0x2 is not exist, a geneal protection fault will happens. My question is how could I bypass this fault, like define my GP handler? if I cannot read a msr, just let it go...

以下是我的模块代码:

#include <linux/init.h>
#include <linux/module.h>

#define LBR 0x2 

static unsigned long long x86_get_msr(int msr)
{
    unsigned long msrl = -1, msrh = -1;

    /* NOTE: rdmsr is always return EDX:EAX pair value */
    asm volatile (
                   "rdmsr;"
                   : "=a"(msrl), "=d"(msrh)
                   : "c"(msr));

    return ((unsigned long long)msrh << 32) | msrl;
}

static int helloworld_init(void) {
    unsigned long long ullRet = -1;

    ullRet = x86_get_msr(LBR);
    printk(KERN_INFO "msr: 0x%08x, ret: 0x%016llx", LBR, ullRet);
    printk(KERN_INFO "hello world!\n");
    return 0;
}


static void helloworld_exit(void) {
    printk(KERN_INFO "see you.\n");

    return;
}

module_init(helloworld_init);
module_exit(helloworld_exit);

以下是消息:

[19353.498488] general protection fault: 0000 [#1] SMP PTI
[19353.498491] Modules linked in: helloworld(POE+) nls_utf8 isofs xt_conntrack ipt_MASQUERADE nf_nat_masquerade_ipv4 nf_conntrack_netlink nfnetlink xfrm_user xfrm_algo xt_addrtype iptable_filter iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack libcrc32c br_netfilter bridge stp llc aufs overlay crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd input_leds snd_intel8x0 snd_ac97_codec serio_raw ac97_bus joydev snd_pcm snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq intel_rapl_perf snd_seq_device snd_timer vboxguest(OE) snd soundcore mac_hid binfmt_misc sch_fq_codel parport_pc ppdev sunrpc lp parport ip_tables x_tables autofs4 btrfs xor zstd_compress raid6_pq dm_mirror dm_region_hash dm_log hid_generic usbhid hid vboxvideo(OE)
[19353.498548]  ttm drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops drm psmouse pata_acpi pcnet32 mii i2c_piix4 ahci libahci video
[19353.498555] CPU: 1 PID: 7379 Comm: insmod Tainted: P           OE    4.15.0-91-generic #92-Ubuntu
[19353.498556] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[19353.498560] RIP: 0010:x86_get_msr+0x25/0x3d [helloworld]
[19353.498561] RSP: 0018:ffffb92e46477c40 EFLAGS: 00010282
[19353.498563] RAX: 0000000000000002 RBX: 0000000000000000 RCX: 0000000000000002
[19353.498564] RDX: 000000000003f8e0 RSI: ffff9e3d4fdba500 RDI: 0000000000000002
[19353.498565] RBP: ffffb92e46477c58 R08: ffff9e3d5fd26080 R09: ffffffff8302c54e
[19353.498566] R10: ffffe11380ccfa40 R11: ffff9e3d5ffd1000 R12: ffffffffc092e03d
[19353.498567] R13: ffff9e3d4fdbaf80 R14: 0000000000000001 R15: ffff9e3ce8adede0
[19353.498569] FS:  00007f6e90008540(0000) GS:ffff9e3d5fd00000(0000) knlGS:0000000000000000
[19353.498571] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[19353.498582] CR2: 000055aabbc97e88 CR3: 00000000b1924006 CR4: 00000000000606e0
[19353.498599] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[19353.498600] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[19353.498601] Call Trace:
[19353.498605]  helloworld_init+0x1f/0x4e [helloworld]
[19353.498609]  do_one_initcall+0x52/0x19f
[19353.498612]  ? _cond_resched+0x19/0x40
[19353.498614]  ? kmem_cache_alloc_trace+0x14e/0x1b0
[19353.498617]  ? do_init_module+0x27/0x213
[19353.498619]  do_init_module+0x5f/0x213
[19353.498621]  load_module+0x16bc/0x1f10
[19353.498624]  ? ima_post_read_file+0x96/0xa0
[19353.498627]  SYSC_finit_module+0xfc/0x120
[19353.498629]  ? SYSC_finit_module+0xfc/0x120
[19353.498632]  SyS_finit_module+0xe/0x10
[19353.498634]  do_syscall_64+0x73/0x130
[19353.498636]  entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[19353.498638] RIP: 0033:0x7f6e8fb2f839
[19353.498639] RSP: 002b:00007ffd5b22f348 EFLAGS: 00000246 ORIG_RAX: 0000000000000139
[19353.498640] RAX: ffffffffffffffda RBX: 000055aabbc957a0 RCX: 00007f6e8fb2f839
[19353.498641] RDX: 0000000000000000 RSI: 000055aababe6d2e RDI: 0000000000000003
[19353.498642] RBP: 000055aababe6d2e R08: 0000000000000000 R09: 00007f6e8fe02000
[19353.498643] R10: 0000000000000003 R11: 0000000000000246 R12: 0000000000000000
[19353.498644] R13: 000055aabbc95760 R14: 0000000000000000 R15: 0000000000000000
[19353.498646] Code: <0f> 32 48 89 45 f0 48 89 55 f8 48 8b 45 f8 48 c1 e0 20 48 0b 45 f0 
[19353.498655] RIP: x86_get_msr+0x25/0x3d [helloworld] RSP: ffffb92e46477c40

推荐答案

我通过添加初始化idt的代码尝试了以下代码,但是即使没有系统日志,Linux也崩溃了...我认为崩溃点应该在指令上的秘密...

I tried the following code, by adding code of initializing the idt, but linux crashed even without any syslog... I think the crash point should be at instruction of lidt...

有任何提示吗?

#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>

#define IDTBASE 0               /* GDT Physical address */
#define IDTSIZE 256             /* Max descriptor count */
#define INTGATE 0x8E00  /* Interruptions */
#define TRAPGATE 0x8F00 /* Syscalls */
#define TASKGATE 0x8500 /* Task switching */

/* segment descriptor */
struct idtdesc {
        u16 offset0_15;
        u16 select;
        u16 type;
        u16 offset16_31;       
}  __attribute__ ((packed));

/* IDTR register */
struct idtr {
        u16 limite;
        u32 base;
}  __attribute__ ((packed));

struct idtr kidtr;

/* IDT table */
struct idtdesc kidt[IDTSIZE] = {{0, 0, 0, 0}};

/* pointer on a free IDT entry */
unsigned int kidtptr = 0;

void default_int(void){printk(KERN_INFO "default_init called\n");}
void k_int0(void){printk(KERN_INFO "k_int0 called\n");}
void k_int1(void){printk(KERN_INFO "k_int1 called\n");}
void k_int2(void){printk(KERN_INFO "k_int2 called\n");}
void k_int3(void){printk(KERN_INFO "k_int3 called\n");}
void k_int4(void){printk(KERN_INFO "k_int4 called\n");}
void k_int5(void){printk(KERN_INFO "k_int5 called\n");}
void k_int6(void){printk(KERN_INFO "k_int6 called\n");}
void k_int7(void){printk(KERN_INFO "k_int7 called\n");}
void k_int8(void){printk(KERN_INFO "k_int8 called\n");}
void k_int9(void){printk(KERN_INFO "k_int9 called\n");}
void k_int10(void){printk(KERN_INFO "k_int10 called\n");}
void k_int11(void){printk(KERN_INFO "k_int11 called\n");}
void k_int12(void){printk(KERN_INFO "k_int12 called\n");}
void k_int13(void){printk(KERN_INFO "k_int13 called\n");}
void k_int14(void){printk(KERN_INFO "k_int14 called\n");}
void k_int15(void){printk(KERN_INFO "k_int15 called\n");}
void k_int16(void){printk(KERN_INFO "k_int16 called\n");}
void k_int17(void){printk(KERN_INFO "k_int17 called\n");}
void k_int18(void){printk(KERN_INFO "k_int18 called\n");}
void k_irq0(void){printk(KERN_INFO "k_irq0 called\n");}
void k_irq1(void){printk(KERN_INFO "k_irq1 called\n");}
void k_irq2(void){printk(KERN_INFO "k_irq2 called\n");}
void k_irq3(void){printk(KERN_INFO "k_irq3 called\n");}
void k_irq4(void){printk(KERN_INFO "k_irq4 called\n");}
void k_irq5(void){printk(KERN_INFO "k_irq5 called\n");}
void k_irq6(void){printk(KERN_INFO "k_irq6 called\n");}
void k_irq7(void){printk(KERN_INFO "k_irq7 called\n");}
void k_irq8(void){printk(KERN_INFO "k_irq8 called\n");}


void init_idt_desc(u32 offset, u16 select, u16 type, struct idtdesc* desc) {
        desc->offset0_15 = (offset & 0xffff);
        desc->select = select;
        desc->type = type;
        desc->offset16_31 = (offset & 0xffff0000) >> 16;
        return;
}


void add_idt_desc(struct idtdesc desc) {
        kidt[kidtptr++] = desc;
        return;
}


void init_idt(void) {
        struct idtdesc desc;
        int i;

        for(i=0;i<IDTSIZE;i++) {
                init_idt_desc((u32)default_int, 0x08, INTGATE, &desc);
                add_idt_desc(desc);
        }

        init_idt_desc((u32)k_int0, 0x08, INTGATE, &kidt[0]);
        init_idt_desc((u32)k_int1, 0x08, INTGATE, &kidt[1]);
        init_idt_desc((u32)k_int2, 0x08, INTGATE, &kidt[2]);
        init_idt_desc((u32)k_int3, 0x08, INTGATE, &kidt[3]);
        init_idt_desc((u32)k_int4, 0x08, INTGATE, &kidt[4]);
        init_idt_desc((u32)k_int5, 0x08, INTGATE, &kidt[5]);
        init_idt_desc((u32)k_int6, 0x08, INTGATE, &kidt[6]);
        init_idt_desc((u32)k_int7, 0x08, INTGATE, &kidt[7]);
        init_idt_desc((u32)k_int8, 0x08, INTGATE, &kidt[8]);
        init_idt_desc((u32)k_int9, 0x08, INTGATE, &kidt[9]);
        init_idt_desc((u32)k_int10, 0x08, INTGATE, &kidt[10]);
        init_idt_desc((u32)k_int11, 0x08, INTGATE, &kidt[11]);
        init_idt_desc((u32)k_int12, 0x08, INTGATE, &kidt[12]);
        init_idt_desc((u32)k_int13, 0x08, INTGATE, &kidt[13]);
        init_idt_desc((u32)k_int14, 0x08, INTGATE, &kidt[14]);
        init_idt_desc((u32)k_int15, 0x08, INTGATE, &kidt[15]);
        init_idt_desc((u32)k_int16, 0x08, INTGATE, &kidt[16]);
        init_idt_desc((u32)k_int17, 0x08, INTGATE, &kidt[17]);
        init_idt_desc((u32)k_int18, 0x08, INTGATE, &kidt[18]);

        init_idt_desc((u32)k_irq0, 0x08, INTGATE, &kidt[32]);
        init_idt_desc((u32)k_irq1, 0x08, INTGATE, &kidt[33]);
        init_idt_desc((u32)k_irq2, 0x08, INTGATE, &kidt[34]);
        init_idt_desc((u32)k_irq3, 0x08, INTGATE, &kidt[35]);
        init_idt_desc((u32)k_irq4, 0x08, INTGATE, &kidt[36]);
        init_idt_desc((u32)k_irq5, 0x08, INTGATE, &kidt[37]);
        init_idt_desc((u32)k_irq6, 0x08, INTGATE, &kidt[38]);
        init_idt_desc((u32)k_irq7, 0x08, INTGATE, &kidt[39]);
        init_idt_desc((u32)k_irq8, 0x08, INTGATE, &kidt[40]);

        kidtr.limite = IDTSIZE*8;
        kidtr.base = IDTBASE;

        printk(KERN_INFO "init_idt called\n");
        kidtr.base = kidt;
        //memcpy((void*)kidtr.base, kidt, kidtr.limite);
        asm volatile ("lidt %0;"
                       : 
                       : "m"(kidtr));

        printk(KERN_INFO "lidt executed called\n");
}


#define LBR 0x2 


static unsigned long long x86_get_msr(int msr)
{
    unsigned long msrl = -1, msrh = -1;

    /* NOTE: rdmsr is always return EDX:EAX pair value */
    asm volatile (
                   "rdmsr;" 
                   : "=a"(msrl), "=d"(msrh) 
                   : "c"(msr));

    return ((unsigned long long)msrh << 32) | msrl;
}

static int helloworld_init(void) {
    unsigned long long ullRet = -1;
    init_idt(); 
    ullRet = x86_get_msr(LBR);
    printk(KERN_INFO "msr: 0x%08x, ret: 0x%016llx", LBR, ullRet);
    printk(KERN_INFO "hello world!\n");
    return 0;
}


static void helloworld_exit(void) {
    printk(KERN_INFO "see you.\n");

    return;
}

module_init(helloworld_init);
module_exit(helloworld_exit);

这篇关于如何处理Linux内核中的常规保护错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆