代码之家  ›  专栏  ›  技术社区  ›  tur1ng

使用setrlimit()设置堆栈大小并引发堆栈溢出/segfault

  •  12
  • tur1ng  · 技术社区  · 14 年前

    为什么现在可以在堆栈上分配具有大小的int数组 8kb foo() ?

    #include <stdio.h>
    #include <sys/resource.h>
    
    void foo(void);
    
    int main() {
     struct rlimit lim = {1024, 1024};
    
     if (setrlimit(RLIMIT_STACK, &lim) == -1)
      return 1;
    
     foo();
    
     return 0;
    }
    
    void foo() {
     unsigned ints[2048];
    
     printf("foo: %u\n", ints[2047]=42);
    }
    
    2 回复  |  直到 14 年前
        1
  •  7
  •   ninjalj    14 年前

    立即设置限制,但仅在尝试分配新堆栈或尝试扩展现有堆栈时才进行检查。一个用于RLIMIT_STACK的grep( or a LXR identifier search

    显然,堆栈的初始大小是filename+env strings+arg strings加上分配给 setup_arg_pages (2.6.33中20页 1 , 2 ,2.6.34上的128 Kb 3

    总而言之:

    initial stack size = MIN(size for filename + arg strings + env strings + extra pages, MAX(size for filename + arg strings + env strings, RLIMIT_STACK))
    

    哪里

    size for filename + arg strings + env strings <= MAX(ARG_MAX(32 pages), RLIMIT_STACK/4)
    

    exec-shield 补丁(Fedora,Ubuntu,…)有一个额外的EXEC_STACK_BIAS “(再增加2 MB以覆盖随机效应。) ,请参见对新函数的调用 over_stack_limit() acct_stack_growth() ( [Ubuntu1] [Ubuntu2] , [Ubuntu3]

    我编辑了原始程序以显示:

    #include <stdio.h>
    #include <sys/resource.h>
    
    void foo(void);
    
    int main(int argc, char *argv[]) {
            struct rlimit lim = {1, 1};
    
    
            if (argc > 1 && argv[1][0] == '-' && argv[1][8]=='l') {
                    printf("limiting stack size\n");
                    if (setrlimit(RLIMIT_STACK, &lim) == -1) {
                            printf("rlimit failed\n");
                            return 1;
                    }
            }
    
            foo();
    
            return 0;
    }
    
    void foo() {
            unsigned ints[32768];
    
            printf("foo: %u\n", ints[2047]=42);
    }
    

    $./rl
    foo: 42
    $./rl -l
    limiting stack size
    Segmentation fault
    $  
    
        2
  •  4
  •   pmg    14 年前

    我想 setrlimit 移动“资源指针”,但在您 exec 程序的新副本。

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/resource.h>
    
    void foo(int chk) {
      unsigned ints[2048];
      ints[2047] = 42;
      printf("foo %d: %u\n", chk, ints[2047]);
    }
    
    int main(int argc, char **argv) {
      char *newarg[] = { "argv[0]", "one", "two" };
      char *newenv[] = { NULL };
      struct rlimit lim;
    
      newarg[0] = argv[0];
      getrlimit(RLIMIT_STACK, &lim);
      printf("lim: %d / %d\n", (int)lim.rlim_cur, (int)lim.rlim_max);
      switch (argc) {
        case 1: /* first call from command line */
          lim.rlim_cur = 65536;
          lim.rlim_max = 65536;
          if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
          newarg[2] = NULL;
          foo(1);
          execve(argv[0], newarg, newenv);
          break;
        case 2: /* second call */
          lim.rlim_cur = 1024;
          lim.rlim_max = 1024;
          if (setrlimit(RLIMIT_STACK, &lim) == -1) return EXIT_FAILURE;
          foo(2);
          execve(argv[0], newarg, newenv);
          break;
        default: /* third call */
          foo(3);
          break;
      }
      return 0;
    }
    

    $ ./a.out 
    lim: 8388608 / -1
    foo 1: 42
    lim: 65536 / 65536
    foo 2: 42
    Killed
    

    为什么在打印限制之前(在调用foo之前)进程会被终止,我不知道。