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

C 使用setrlimit()设置堆栈大小并引发堆栈溢出/SEGFULT,c,linux,stack,segmentation-fault,setrlimit,C,Linux,Stack,Segmentation Fault,Setrlimit,在下面的示例中,我尝试将stacksize设置为1kb 为什么现在可以在foo()中在堆栈上分配大小为8kb的整数数组 #包括 #包括 void foo(void); int main(){ 结构rlimit lim={1024,1024}; if(setrlimit(RLIMIT_堆栈和lim)=-1) 返回1; foo(); 返回0; } void foo(){ 无符号整数[2048]; printf(“foo:%u\n”,ints[2047]=42); } 我认为setrlimit会移动“

在下面的示例中,我尝试将stacksize设置为1kb

为什么现在可以在
foo()
中在堆栈上分配大小为
8kb
的整数数组

#包括
#包括
void foo(void);
int main(){
结构rlimit lim={1024,1024};
if(setrlimit(RLIMIT_堆栈和lim)=-1)
返回1;
foo();
返回0;
}
void foo(){
无符号整数[2048];
printf(“foo:%u\n”,ints[2047]=42);
}

我认为
setrlimit
会移动“资源指针”,但在执行程序的新副本之前不会应用新的限制

#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;
}
#包括
#包括
#包括
#包括
空富(内部通道){
无符号整数[2048];
ints[2047]=42;
printf(“foo%d:%u\n”,chk,ints[2047]);
}
int main(int argc,字符**argv){
char*newarg[]={“argv[0]”、“一”、“二”};
char*newenv[]={NULL};
struct-rlimit-lim;
newarg[0]=argv[0];
getrlimit(RLIMIT_堆栈和lim);
printf(“lim:%d/%d\n”,(int)lim.rlim\u cur,(int)lim.rlim\u max);
开关(argc){
案例1:/*来自命令行的第一次调用*/
lim.rlim_cur=65536;
lim.rlim_max=65536;
if(setrlimit(RLIMIT_STACK,&lim)=-1)返回EXIT_失败;
newarg[2]=NULL;
傅(1),;
execve(argv[0],newarg,newenv);
打破
案例2:/*第二次呼叫*/
lim.rlim_cur=1024;
lim.rlim_max=1024;
if(setrlimit(RLIMIT_STACK,&lim)=-1)返回EXIT_失败;
富(2),;
execve(argv[0],newarg,newenv);
打破
默认值:/*第三次呼叫*/
傅(3);
打破
}
返回0;
}
和试运行:

$ ./a.out lim: 8388608 / -1 foo 1: 42 lim: 65536 / 65536 foo 2: 42 Killed 美元/年 lim:8388608/-1 foo 1:42 lim:65536/65536 foo 2:42 被杀死的
我不知道为什么在打印限制之前(以及调用foo之前)进程会被终止。

限制会立即设置,但仅在尝试分配新堆栈或尝试扩大现有堆栈时才被检查。内核源代码上的RLIMIT_STACK()的grep应该说明这一点

显然,堆栈的初始大小是文件名+env strings+arg strings加上
setup\u arg\u pages
上分配的一些额外页面所需的大小(2.6.33中有20页,2.6.34中有128 Kb)

总之:

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

其结果是:

$./rl
foo: 42
$./rl -l
limiting stack size
Segmentation fault
$  

我怀疑类似的情况,只是尝试了
fork()
,没有任何区别。我不明白为什么setrlimit()只影响通过
exec
生成的进程,而不影响父进程,但事实似乎确实如此。对于GDB,我在“foo 2:42”行之后得到“程序正常退出”-不被终止,不被终止segfault@tur1ng:尝试添加
newarg[0]=argv[0]在main的开头。我怀疑你的二进制文件不是“a.out”,它是正确的a.out。即使是
ulimit-s1
也不会导致错误。@pmg,您使用的是什么内核版本/OS专用酱汁?我想我们在这里谈论的可能是一个移动的目标:)我也不能用2.6.31(Ubuntu)复制你的结果。不,事实上,我能够扩展现有的堆栈。我现在就像一条狗,在这个问题上一根骨头也放不下。@Tim Post:你确定这堆骨头确实长了吗?请参阅我编辑的答案,初始堆栈上有一些额外的空间。是的,我将这两种情况扩展到16k,结果相同。@Tim Post:在2.6.33-x86上将其扩展到>80KB,在2.6.34上扩展到>128Kb+在修正后尝试设置
rlimit_堆栈
,可能会导致失败或相关问题。另请看《红帽》谢谢,我现在沉迷于找出为什么这不能像《男人》(2)setrlimit中的广告那样起作用。幸运的是,gcc允许您指定堆栈大小:)这是一个比目前更受欢迎的问题。有趣。
size for filename + arg strings + env strings <= MAX(ARG_MAX(32 pages), RLIMIT_STACK/4)
#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
$