RSS Feed

August, 2012

  1. 从info域名换至org域名

    August 21, 2012 by xudifsd

    买info域名一年了,到了续费的时候了,不过续费时就ORZ了。续费竟然需要90大洋!com/org/net域名买和续费都才70,一个info域名为什么要这么贵?当年买info域名是就是因为便宜,才30大洋,而且也觉得应该这么便宜,所以根本没考虑续费的价格就买下了。结果是跳进了Godaddy挖的大坑啊,太坑爹了。奉劝读者不要买info域名。

    另外这次转域名也学会很多东西啊,至少明白.htaccess文件是干什么的了。换的期间找到两篇文章(12)希望对以后想换的人有帮助,另外订阅了我的RSS的读者需要换下feed了。把xudifsd.info/blog/feed/改成xudifsd.org/blog/feed/即可。


  2. 关于linux的堆栈实验

    August 8, 2012 by xudifsd

    在《UNIX操作系统设计》第11章的第6题写道:

    Write a program that attaches shared memory too close to the end of its stack, and let the stack grow into the shared memory region. When does it incur a memory fault?

    出于对结果的兴趣写了如下程序:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    char *get_esp() {
            char *esp;
            __asm__("movl %%esp, %%eax\n" :"=a"(esp):);
            return esp;
    }
    void re(int dummy) {
            re(dummy);
    }
    int main(int argc, char *argv[]) {
            size_t size = 4096;
            int mask = ~(0xfff);
            char *wanted_addr;
            char *esp;
            char *addr;
    
            esp = get_esp();
            wanted_addr = ((int)esp & mask) - 0x1000;
            printf("esp = 0x%x\nwanted_addr = 0x%x\n", esp, wanted_addr);
    
            addr = (char *)mmap(wanted_addr, size, PROT_WRITE| PROT_READ, \
    		MAP_PRIVATE| MAP_ANONYMOUS, -1, 0);
            if (addr == MAP_FAILED) {
                    perror("mmap");
                    exit(1);
            }
            printf("addr = 0x%x\n", addr);
            re(1);
    }

    总体来说该程序就是利用mmap分配一个与esp所在页框相邻且地址更低的页框,然后调用一个递归函数消耗掉所有的堆栈空间,看进程会在什么时候崩溃。编译后在gdb下运行结果如下:

    (gdb) r
    Starting program: /home/xudifsd/a.out 
    esp = 0xbffff0c4
    wanted_addr = 0xbfffe000
    addr = 0xb7fd9000
    
    Program received signal SIGSEGV, Segmentation fault.
    0x08048497 in re ()
    (gdb) p $esp
    $1 = (void *) 0xbf800000

    可以看出程序在esp为0xbf800000处收到SIGSEGV信号,这和mmap得到的地址0xb7fd9000相差太多所以不可能是因为写到mmap分配的页面造成的错误,然后猜测可能是写到动态库,但是lld结果如下:

    	linux-gate.so.1 =>  (0xb7725000)
    	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7563000)
    	/lib/ld-linux.so.2 (0xb7726000)

    比mmap分配的地址还要低,所以也不可能是写到了动态库的代码部分产生的错误。然后计算了下0xbffff000 – 0xbf800000差不多等于8M才发现这是由于linux默认的堆栈大小为8M造成的(用ulimit -s查看),于是给代码加了段函数:

    void set_limit() {
            struct rlimit rl;
            rl.rlim_cur = 4294967295;
            rl.rlim_max = 4294967295;
            if (setrlimit(RLIMIT_STACK, &rl) == -1) {
                    perror("set_limit");
                    exit(1);
            }
    }

    再运行发现程序在0xb8001000收到SIGBUS信号退出。虽然比前一次有进步但是也没有写到mmap分配的那个页框,计算0xbffff000 – 0xb8001000得到差不多127M的空间,但是完全不清楚为什么进程会在这收到信号。

    之后直接在shell中运行ulimit -s 4194304再启动程序会发现程序直接被SIGKILL杀死,但是在gdb中也没法找出最后一刻的esp值,因此我以为是写到了只读的共享库代码页造成的,所以将re函数改写成为:

    void re(int dummy) {
            printf("esp == 0x%x\n", get_esp());
            re(dummy);
    }

    这样修改后运行程序会造成系统崩溃,所以如果想实验请先保存所有资料。

    运行时会发现程序的esp值能够低于前面共享库的地址,也就是说程序会将共享库的空间也占据,即使它是只读的代码段。我的系统内存加swap都才1.5G(表鄙视,现在市面上都买不到DDR2的内存条了,所以我也无能为力。。),所以程序运行到0xaa000000左右系统就已经无响应了,所以如果有内存大的读者愿意试可以将结果告诉下我。

    看来最初的问题是没办法回答了,因为那书基于的是System V系统,而现在linux系统变化了很多,就连这么多实验也没办法找出答案,如果有读者知道还希望能告诉我。

    问题

    即使是实验做完了,这些问题也解决不了,看来还要等以后会更多东西时才能给出答案吧。

    1. 为什么mmap分配这么低的一个空间?内核代码中有限制?
    2. 执行setrlimit调用和在shell下使用ulimit有什么区别,为什么效果差这么多?
    3. 为什么第二次会收到SIGBUS信号而第三次没有在相同地址收到信号,而是收到SIGKILL信号?
    4. 为什么修改了re函数后程序能够覆盖只读的代码段,难道我的代码错了,或者说push指令可以不管页面的只读属性?
    5. 为什么会收到SIGKILL信号?