Mctrain's Blog

What I learned in IT, as well as thought about life

| Comments

总的来说这周过得是比较颓废的,花了太多时间在金庸上,晚睡早起的,不过我并不后悔,没有什么好后悔的,做了就是做了,而且过程还挺开心的,好好制定接下来的计划并实行就好了。

看了射雕,真的很羡慕小说中人物的生活,很喜欢靖哥哥和蓉儿,有的时候我会想,如果真有一个那样子的世界,而我又身处其中,那又会怎么样呢?我也能像郭靖那样这么真诚地对待自己和他人吗?从一开始我就感觉很讨厌杨康,可是看完了回头想想杨康真的做的就完全不对吗?作为一个人多为自己考虑又哪里错了呢?喜欢郭靖还有很重要的原因是他功夫好,可是有时候又真的很为他担心,太过于坚持原则。说到底,我还是最喜欢蓉儿和后期的靖哥哥,真的真的好羡慕他们两人,我知道这是虚构,可是为什么就不能相信世界上真的有这样的一对呢~还有,我在想,在现实生活中,真的能像郭靖那样真诚地对待他人吗?路上看到需要帮助的人你能毫不犹豫地给予施舍?给别人的承诺就算再难也要实现?为了心爱的人能付出所有的一切,包括自己的生命?可是世间又是那么地充满不公,充盈着欺瞒与腐败,作为一个如此普通的人,没有拯救世界的能力,又何来如此真诚而无条件的信心呢?所以我有时憧憬着那些武林中的生活,固然世间充满着不公,固然人依然是如此的渺小,很多时候却更有可能单纯依靠努力改变一些什么。说不清楚什么,或许是虚拟世界的完美性的虚构带来的幻觉吧……

武林中的世界将要告一段落,回到现实生活中,这周主要的工作是两篇paper的rebuttal和依旧的读paper,从PC的review其实可以从一定程度上看出最后的结果了,不过依然带着些侥幸心理,还是要等到最后吧。在后来重新搭建CFIMon的时候发现虽然只是经过了短短2、3个月,很多东西依然忘记了,主要还是因为1.理解的不够深,2.没有很好地记录下来吧。比如那个禁止linux地址空间布局随机化的

    echo 0 > /proc/sys/kernel/randomize_va_space    

如果当时能记下来这次就能省去好多时间了。这就是为什么今年”记笔记”是我一个很大的计划的原因了。真的越来越感觉到自己记忆的易失和笔头的重要性了。

还有这周印象很深的是在讨论CFIMon处理signal的问题的时候,斌哥问我关于linux中对signal处理的问题,说来惭愧,当时在写这篇paper的时候我对这个还是一知半解的,甚至到现在还把signal的处理和interrupt的处理混起来了。希望能在这里把这东西说清楚:

当一个进程需要向另一个进程发送一个signal时,它是通知kernel产生这个signal,并将接受方进程的某个flag(ULK中说的是TIF_SIGPEDING)置好,当然这个时候并不是能马上告诉接受进程有signal需要处理,而是像ULK上说的:

    The kernel checks for the existence of pending signals every time it finishes handling an interrupt or an exception.

也就是说只有在某个进程运行时需要处理中断或异常即将返回该进程执行时才会检查是否有未处理的signal,再对其进行处理。对于signal的处理主要有三种情况:

    1,ignore
    2,the default action,通常是SIGSTOP
    3,用户注册的相对应的signal handler

对于前面两种情况很容易理解和实现,这里主要要说的是第三种情况,这个情况的麻烦在于它要先从kernel态切换到user态执行handler,之后再切回kernel态,直到回到进程在进入kernel之前的状态,而这些就要很仔细地考虑栈里面数据和进程状态的维护了。在ULK中用了一张图来表示整个过程: Catching a Signal

简单来说整个流程是这样的:在处理完中断或异常将从kernel态回到用户态之前,kernel会调用do_signal()函数,在这个函数里面会调用handle_signal()函数,这个函数首先创建用户态所需要的栈环境,这个栈是长这样的: Frame on the User Mode Stack

其实主要就是把return address改成用户自行定义的signal handler的入口,然后进入用户态,执行相应的代码,当执行结束之后,会根据栈顶的pretcode,调用一个sigreturn()或rt_sigreturn()系统调用,回到kernel态,执行流继续下去。其实这里面还有很多细节的,包括那些栈里面内容的含义,如何处理嵌套signal等等,这些需要更仔细地参阅相关资料了。

最后依然是写写这周看的paper,这周本来是想把Oakland’11扫一遍的,不过自己看paper的效率实在是低,加上觉得前面看的几篇确实都蛮有意思的,就多花了些时间,这个专题还是下周来写吧,另外这周给自己选的paper是6.858的preparation的第一篇,叫

    Baggy Bounds Checking: An Efficient and Backwards-Compatible Defense against Out-of-Bounds Errors

是发在usenix security 2009上的,主要是因为它是课程的pre-reading,其实我自己本身对这方面不是很熟悉,也不是太感兴趣,在这里就简单介绍下吧:

这篇主要是做memory bounds checking,在文中首先提出要做bound checking主要要考虑两点,第一效率高,第二向后兼容性,这里先归纳下传统memory errors detection的方法:

    static technique;
    dynamic technique (with or without source code);
    control flow integrity, taint trcking;
    system support (fat pointer, splay tree to map memory...)

这些方法按照作者的说法要不效率太低,要不需要改变整个系统指针结构的layout,不能向后兼容,要么检查的错误有限制(其实本文也有很大的限制啦)…而作者做这个Baggy bounds checker的最大的insight是,现在linux里面很流行的buddy allocator的特性,所有allocate出来的页的大小都是2的指数大小,于是对于内存区域size和allignment的限制能够很大程度地减小内存的利用,提高检察效率。举个例子,对于传统的保存pointer信息的结构,至少需要8个bytes的空间(4位保存pointer起始地址,4位保存pointer指向内存的大小)而对于baggy来说,只需要一个byte的大小(e = log2(size))。至于pointer的起始地址,由于在baggy系统里内存被分为slot-size(implementation中是16-bytes)的一个个slots,于是

    size = 1 << e
    base = p & ~(size-1)

而记录这个e的table是一个占用内存1/slot-size大小的array,每一个slot一个byte表示,于是举个例子:

    对于指针运算:p' = p + i
    bound的检查:size = 1 << table[p>>slot_size]
                 base = p & ~(size-1)
                 p' >= base && p' - base < size
    检查的优化:(p~p')>>table[p>>slot_size] == 0

这个优化也很好理解,对于计算出的指针p’,如果它不越界,当且仅当它和p的不同只发生在e least significant bits上。这个baggy bounds checker的整个流程是这样的: Overall System Architecture

当然这中间还有很多细节,我不想一一列出来讲,而且说实话我也看的不是很懂,最后想提提这个paper的limitation,这个最后作者也有说到,除了它列出的几点:

    arithemtic on integers holding addresses is unchecked
    does not address temporal memory safety violations
    cannot protect from memory errors in subobjects such as structure fields

我还有几点不是很确定算不算,第一个,在它的实现中slot-size为16bytes,也就是说所有memory allocator出来的内存大小都要大于16bytes(否则会有两个objects指在同一个slot里面),那么对于比如integer指针,岂不是很浪费?padding过多会造成大量的内存浪费,当然按照作者的说法这个可以做一个权衡,但是如何权衡或许用户并不是很清楚。第二,对于non-instrumented的第三方library的checking,按照作者的说法将默认size设为2^31,我不懂这样做会不会有很大的副作用,另外,只能在修改过的应用程序中检查memory bounds violation,对于library中的指针运算依然没办法,而library的code我想应该比用户自己的application多出很多吧,也不见得vulnerability会少啊。

另外在MIT 6.858的lecture-2里面也是讲相关知识的,这里想补充一些”How to prevent buffer overflows”

    Approach 0: use a type-save language (Java, C#, Python..)
    Approach 1: try to find bugs (analysis tools, fuzzers)
    Approach 2: canaries (StackGuard, gcc's SSP)
    Approach 3: bounds checking
    Approach 4: non-executable memory (AMD's NX bit, Windows DEP, W^X...)
    Approach 5: randomized memory address (ASLR, stack randomization...)

下周打算把Oakland’11完成,至于其它任务吗,还没想好咯。

Comments