为什么在 C 中使用克隆函数需要这个指针运算?

发布于 2024-12-22 23:23:52 字数 2048 浏览 2 评论 0原文

我正在尝试在 C 中使用 clone() 函数,并且不确定第二个参数如何工作。根据 clone() 手册页:

   The child_stack argument specifies the location of the stack used by  the
   child  process.  Since the child and calling process may share memory, it
   is not possible for the child process to execute in the same stack as the
   calling  process.  The calling process must therefore set up memory space
   for the child stack and pass a pointer to this space to clone().   Stacks
   grow downwards on all processors that run Linux (except the HP PA proces‐
   sors), so child_stack usually points to the topmost address of the memory
   space set up for the child stack.

遵循本文<的评论中的建议之后< /a>,我已经能够使用这个 C 程序获得一个简单的示例:

#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <assert.h>

#define SIZE 65536

int v1;

int run(void *arg) {
  v1 = 42;
  return 0;
}

int main(int argc, char **argv) {
  void **child_stack;
  int pid, rc, status;
  v1 = 10;
  child_stack = (void **) malloc(SIZE);
  assert(child_stack != NULL);
  printf("v1 before: %d\n", v1);

  pid = clone(run, child_stack + SIZE/sizeof(void **), CLONE_VM, NULL);
  //pid = clone(run, child_stack + SIZE, CLONE_VM, NULL);

  assert(pid != -1);
  status = 0;
  rc = waitpid(pid, &status, __WALL);
  assert(rc != -1);
  assert(WEXITSTATUS(status) == 0);
  printf("v1 after:  %d\n", v1);
  return 0;
}

但我很困惑为什么 clone 行中的特定指针算术是必要的。鉴于根据 clone 文档,堆栈应该向下增长,我明白为什么您应该在传入 malloc 返回的指针之前添加一个值。但是我预计您会添加 malloc 的字节数,而不是该值除以 8(在 64 位系统上),这似乎是实际有效的。无论我将 SIZE 定义为什么,上面的代码似乎都能正常工作,但是如果我使用注释版本(这正是我期望的工作方式),我会收到所有 SIZE 值的分段错误高于某个阈值。

那么,任何人都明白为什么给定的克隆行有效,但注释行却不起作用?

至于为什么我首先使用clone,而不是fork或pthreads,我试图使用它的一些高级沙箱功能来防止不受信任的进程突破 chroot 监狱,如此处所述。

I'm trying to use the clone() function in C, and am uncertain of how the second argument works. Per the clone() man page:

   The child_stack argument specifies the location of the stack used by  the
   child  process.  Since the child and calling process may share memory, it
   is not possible for the child process to execute in the same stack as the
   calling  process.  The calling process must therefore set up memory space
   for the child stack and pass a pointer to this space to clone().   Stacks
   grow downwards on all processors that run Linux (except the HP PA proces‐
   sors), so child_stack usually points to the topmost address of the memory
   space set up for the child stack.

After following suggestions in the comments on this article, I've been able to get a simple example working using this C program:

#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <assert.h>

#define SIZE 65536

int v1;

int run(void *arg) {
  v1 = 42;
  return 0;
}

int main(int argc, char **argv) {
  void **child_stack;
  int pid, rc, status;
  v1 = 10;
  child_stack = (void **) malloc(SIZE);
  assert(child_stack != NULL);
  printf("v1 before: %d\n", v1);

  pid = clone(run, child_stack + SIZE/sizeof(void **), CLONE_VM, NULL);
  //pid = clone(run, child_stack + SIZE, CLONE_VM, NULL);

  assert(pid != -1);
  status = 0;
  rc = waitpid(pid, &status, __WALL);
  assert(rc != -1);
  assert(WEXITSTATUS(status) == 0);
  printf("v1 after:  %d\n", v1);
  return 0;
}

But I'm confused as to why the particular pointer arithmetic in the clone line is necessary. Given that according to the clone docs the stack is supposed to grow downward, I see why you should add a value to the pointer returned by malloc before passing it in. But I'd expect that you'd add the number of bytes malloc'd, instead of that value divided by 8 (on a 64-bit system), which is what seems to actually work. The code above seems to work fine regardless of what I define SIZE as, but if I use the commented version instead, which is what I'd expect to work, I get a segmentation fault for all SIZE values above a certain threshold.

So, anyone understand why the given clone line works, but the commented one doesn't?

As for why I'm using clone to begin with, instead of fork or pthreads, I'm trying to use some of its advanced sandboxing features to prevent an untrusted process from breaking out of a chroot jail, as described here.

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

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

发布评论

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

评论(4

只等公子 2024-12-29 23:23:52

问题是您已将 child_stack 声明为 void ** (指向 void 指针的指针),而它实际上是指向用于堆栈的原始内存的指针(没有 C 类型)。因此,如果您只是将其声明为 char *intptr_t 会更有意义,在这种情况下,您可以直接进行指针算术(传递 child_stack + SIZE),而不必纠正错误的类型。

请注意,所写的更正是不正确的(应该是 / sizeof(void *) 而不是 / sizeof(void **)),但它可以正常工作为sizeof(void **) == sizeof(void *)

The problem is that you've declared child_stack as a void ** (a pointer to void pointers), when its really a pointer to raw memory to be used for a stack (which doesn't have a C type). So it would make more sense if you just declared it as a char * or intptr_t instead, in which case you could do the pointer arithmetic directly (passing child_stack + SIZE) rather than having to correct for the incorrect type.

Note that the correction as written is incorrect (should be / sizeof(void *) rather than / sizeof(void **)), but it works out ok as sizeof(void **) == sizeof(void *) on most machines

挽清梦 2024-12-29 23:23:52

将整数值 V 添加到 T* 类型的指针会将内存地址增加 V*sizeof(T)。由于代码中的 child_stack 类型为 void**,因此 child_stack+SIZE 实际上意味着内存地址增加了 SIZE* sizeof(void*) 字节。

Adding an integer value V to a pointer of type T* increments the memory address by V*sizeof(T). Since child_stack in your code has type void**, child_stack+SIZE in effect means that the memory address is increased by SIZE*sizeof(void*) bytes.

把梦留给海 2024-12-29 23:23:52

对于指针算术,在确定实际内存偏移量时会合并所指向类型的大小,例如:

int a[2] = {1, 2};
int* p = a;

printf("%x: %x\n", &a[0], p);
printf("%x: %x\n", &a[1], p + 1);

在这种情况下,p 的值不仅仅是 p< 的地址/code> + 1,它是 p + 1 * sizeof(int) 的值(指向的类型的大小)。考虑到这一点,当您想要偏移一定数量的字节时,您需要将偏移量除以要修改的指针类型的大小。在您的情况下,您指向的类型是 void*,因此更准确的说法是:

pid = clone(run, child_stack + SIZE/sizeof(void *), CLONE_VM, NULL);

您可以使用以下方式可视化此行为:

int SIZE = 65536;
void** child_stack = (void **) malloc(SIZE);

void** child_stack_end = child_stack + SIZE;
void** child_stack_end2 = child_stack + SIZE / sizeof(*child_stack);

printf("%d\n", (intptr_t)child_stack_end - (intptr_t)child_stack); // "262144"
printf("%d\n", (intptr_t)child_stack_end2 - (intptr_t)child_stack); // "65536"

With pointer arithmetic, the size of the type pointed to is incorporated when determining the actual memory offset, take for example:

int a[2] = {1, 2};
int* p = a;

printf("%x: %x\n", &a[0], p);
printf("%x: %x\n", &a[1], p + 1);

In this case, the value of p isn't just address of p + 1, it's the value of p + 1 * sizeof(int) (the size of the type pointed to). To account for this, when you want to offset some number of bytes, you need to divide the offset by the size of the pointer type you're modifying. In your case, the type you're pointing to is void*, so it may be more accurate to say:

pid = clone(run, child_stack + SIZE/sizeof(void *), CLONE_VM, NULL);

You can visualize this behavior with something like:

int SIZE = 65536;
void** child_stack = (void **) malloc(SIZE);

void** child_stack_end = child_stack + SIZE;
void** child_stack_end2 = child_stack + SIZE / sizeof(*child_stack);

printf("%d\n", (intptr_t)child_stack_end - (intptr_t)child_stack); // "262144"
printf("%d\n", (intptr_t)child_stack_end2 - (intptr_t)child_stack); // "65536"
枉心 2024-12-29 23:23:52

child_stack + SIZE 指向您分配的数据末尾的末尾,因此使用该位置作为堆栈开头的分段错误并不奇怪。您是否尝试过child_stack + SIZE - 1

child_stack + SIZE points to one past the end of the end of the data you allocated, so a segmentation fault for using that location as the start of the stack is not that surprising. Have you tried child_stack + SIZE - 1?

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