Linux异常表

一、为什么需要异常表?

    处于内核态的程序有下面四种情况会产生缺页异常:

    1、内核试图访问属于进程地址空间的页,但是,该页对应的页框不存在或者内核试图去访问一个只读的页,分别对应“请求调页”和“写时复制”两种情况。

    2、内核寻址到属于内核地址空间的页,但是相应的页表项没有被初始化,这个对应“非连续内存区访问”。

    3、内核代码包含编程错误,函数执行产生异常,这是一个内核漏洞。

    4、系统调用服务例程试图读写一个内存区,而该内存区的地址是通过系统调用参数传递过来的,但确不属于进程的地址空间。

    前面两种情况缺页异常处理程序很容易识别出来,但是后面两种情况却比较难区分。为了识别出后面的两种情况,linux内核专门建立了一个异常表来辨别后两种情况。

二、linux怎样实现异常表?

     异常表存放了内核服务例程访问进程地址空间的每条指令的地址。这个表存放在内核代码段的__ex_table节,其起始和终止地址由C编译器产生的两个符号__start__ex_table和__stop__ex_table来标识,在linux内核链接脚本文件中将每个目标文件中的__ex_table节合并,并定义了__start__ex_table和__stop__ex_table来标识。

  __start___ex_table = .;   //异常表
  __ex_table : { *(__ex_table) }
  __stop___ex_table = .;

     此外,每个动态装载的内核模块包含自己的局部异常表,当模块被加载进内核时,这个表也被装入内存。

     每个异常表的表项都是一个exception_table_entry结构:

struct exception_table_entry
{
    unsigned long insn, fixup;
};

     insn是访问进程地址空间的指令的线性地址。当存放insn单元中的指令所触发的缺页异常发生时,fixup就是要调用的汇编指令代码地址,通常这个汇编代码强制服务例程返回一个出错码给用户态进程。

     一般通过下面的汇编伪指令向异常表中插入一个表项:

.section __ex_table, “a”
    .long faulty_instruction_address, fixup_code_address
.previous

     faulty_instruction_address是访问进程地址空间指令的地址,fixup_code_address是faulty_instruction_address指向的指令所引起异常的修正代码,通常是给用户程序返回错误码。

     例如在访问进程地址空间的内核代码中:

__get_user_1:
    GET_THREAD_INFO(%edx)
    cmpl TI_addr_limit(%edx),%eax
    jae bad_get_user
1:    movzbl (%eax),%edx
    xorl %eax,%eax
    ret

__get_user_2:
    addl $1,%eax
    jc bad_get_user
    GET_THREAD_INFO(%edx)
    cmpl TI_addr_limit(%edx),%eax
    jae bad_get_user
2:    movzwl -1(%eax),%edx
    xorl %eax,%eax
    ret

__get_user_4:
    addl $3,%eax
    jc bad_get_user
    GET_THREAD_INFO(%edx)
    cmpl TI_addr_limit(%edx),%eax
    jae bad_get_user
3:    movl -3(%eax),%edx
    xorl %eax,%eax
    ret

bad_get_user:
    xorl %edx,%edx
    movl $-14,%eax
    ret

.section __ex_table,"a"
    .long 1b,bad_get_user
    .long 2b,bad_get_user
    .long 3b,bad_get_user
.previous

     从上面的汇编代码中可以知道,真正访问进程地址空间的指令是标号1,2,3处的指令,所以只需要把这三处的指令地址放到异常表中就可以了,bad_get_user是处理这个三条指令引起异常的修改代码。

三、怎样通过异常表来判断异常类型?

     当内核态发生缺页异常时,缺页异常处理程序先排除了前面两种情况,然后检查异常表:如果表中包含产生异常的指令地址,那么这个错误就是由非法的系统调用参数引起的,也就是第四种情况,否则,就是由某一更严重的内核bug引起的,即第三种情况。

posted @ 2013-11-18 10:47  在于思考  阅读(3798)  评论(1编辑  收藏  举报