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

发布于 2024-10-01 12:46:01 字数 411 浏览 5 评论 0原文

在下面给出的示例中,我尝试将堆栈大小设置为 1kb。

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

#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);
}

In the given example below I try to set the stacksize to 1kb.

Why is it now possible to allocate an array of ints on the stack with size 8kb in 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);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

水中月 2024-10-08 12:46:01

该限制会立即设置,但仅在尝试分配新堆栈或尝试增加现有堆栈时进行检查。内核源代码上的 RLIMIT_STACK(或 LXR 标识符搜索)的 grep 应该告诉我们。

显然,堆栈的初始大小是文件名 + env 字符串 + arg 字符串加上在 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)

,带有 Ingo Molnar 的 exec-shield 补丁的内核(Fedora、Ubuntu 等)还有一个额外的 EXEC_STACK_BIAS “(多出 2MB 来覆盖随机化效果。)”,请参阅 acct_stack_growth() 对新函数 over_stack_limit() 的调用 ([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
$  

The limit is set immediately but only checked when trying to allocate a new stack or trying to grow the existing stack. A grep for RLIMIT_STACK (or a LXR identifier search) on the kernel sources should tell.

Apparently, the initial size of the stack is whatever is needed to the filename + env strings + arg strings plus some extra pages allocated on setup_arg_pages (20 pages in 2.6.33 1,2, 128 Kb on 2.6.34 3).

In summary:

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

where

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

Additionally, kernels with Ingo Molnar's exec-shield patch (Fedora, Ubuntu, ...) have an additional EXEC_STACK_BIAS "(2MB more to cover randomization effects.)", see the call to the new function over_stack_limit() from acct_stack_growth() ([Ubuntu1], [Ubuntu2], [Ubuntu3]).

I've edited the original program to show this:

#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);
}

Which results in:

$./rl
foo: 42
$./rl -l
limiting stack size
Segmentation fault
$  
要走就滚别墨迹 2024-10-08 12:46:01

我认为 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;
}

并进行测试运行:

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

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

I think setrlimit moves the "resource pointers" but doesn't apply the new limits until you exec a new copy of the program.

#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;
}

And a test run:

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

Why the process gets killed before printing the limits (and before calling foo), I don't know.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文