c - 原始克隆系统调用

标签 c linux linux-kernel fork system-calls

我正在尝试使用原始克隆系统,但找不到任何合适的文档。 我试图编写一个小程序来尝试它,但这最终导致了段错误。

我不明白我哪里错了。

这是一个小应用程序:

define STACK_SIZE 0x10000
define BUFSIZE 200

#define _GNU_SOURCE

void hello (){
    fprintf(stderr,"Hello word\n"); 
    _exit(0); 
}


int main()  
{

int res; 
void *stack = mmap(0, STACK_SIZE, PROT_READ|PROT_WRITE,
                       MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  pid_t ptid, tid; 

  printf("Stack %p\n", stack + STACK_SIZE);
  memset(stack, 0, STACK_SIZE); 

  res= syscall(SYS_clone,CLONE_SIGHAND|CLONE_FS|CLONE_VM|CLONE_FILES,stack + STACK_SIZE, &tid,&ptid,NULL );

  if (!res)
      hello(); 

  printf("Clone result %x\n", res); 
  waitpid(-1, NULL, __WALL); 

 return 0; 
}

最佳答案

child 从空堆栈中弹出返回地址,从未映射的地址读取。

注意:答案假定 x86_64 ISA,其中 call 将返回地址放在堆栈上。将其与 AArch64 进行对比,其中链接寄存器可能保存返回地址。

详情

根据 clone 的手册页,“子进程中的执行从调用点继续”。然而,尽 pipe 项和父项最初执行相同的指令,但它们不共享堆栈,并且 ret 指令在父项和子项中的行为不同。

在 GDB 下运行应用程序显示 SIGSEGV 发生在 syscall 函数的 ret 指令处。

Thread 2 received signal SIGSEGV, Segmentation fault.
[Switching to LWP 415]
syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:40
(gdb) x/i $pc
=> 0x7ffff7ee0745 <syscall+37>: retq   

ret 从堆栈中弹出返回地址。堆栈为空的子级无法执行它,而父级成功返回到 main 函数。

您可以通过提供 stack + STACK_SIZE - 8 作为第三个系统调用参数来修复未映射的访问。然而,潜在的问题仍然存在:零初始化堆栈不存储返回地址。下图说明了这个问题。

simplified syscall syscall+... 的意思是“系统调用中的某处”,以免使地址困惑。

解决方案

Macattack 的回答已经提到了克隆的包装器。按照 uclibc 的思路实现特定于拱门的包装器一个。

关于c - 原始克隆系统调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17604458/

相关文章:

c - 如何接收 (void *)0x12345678 参数?

缓存未命中?我怎么能看到那个?

linux - 从私有(private) docker 注册表下载 docker 镜像后可以重新分发吗?

linux - 查找海湾合作委员会位置

linux - 如何测试内核是否有内核 panic ?

Linux内核模块没有init和exit函数

c - 在 Visual Studio 2010 中从 dll 崩溃中释放主程序内存

python - argc 始终为零

php - 使用 PHP 的 exec() 函数重定向 shell 命令的输出

linux - 如何以编程方式在 Linux 中确定物理 RAM 大小?