我正在尝试使用原始克隆系统,但找不到任何合适的文档。 我试图编写一个小程序来尝试它,但这最终导致了段错误。
我不明白我哪里错了。
这是一个小应用程序:
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
作为第三个系统调用参数来修复未映射的访问。然而,潜在的问题仍然存在:零初始化堆栈不存储返回地址。下图说明了这个问题。
syscall+...
的意思是“系统调用中的某处”,以免使地址困惑。
解决方案
Macattack 的回答已经提到了克隆的包装器。按照 uclibc 的思路实现特定于拱门的包装器一个。
关于c - 原始克隆系统调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17604458/