【问题标题】:Raw clone system call not working correctly原始克隆系统调用无法正常工作
【发布时间】:2017-07-17 03:24:20
【问题描述】:

我正在尝试使用原始克隆系统调用来避免将 pid 0 代码重构为函数。 Linux 需要 16 字节的堆栈,另外,libc 保留 16 位大概用于存储 ptid 和 ctid。下面的代码创建一个对齐的堆栈,然后从子级退出。在等待 libc 的包装器克隆的孩子之后,我使用了具有相同缓冲区的原始系统调用,但每次程序在使用原始系统调用时出现段错误。附件是 strace 的输出,除非我忽略了任何显示系统调用参数两次相同的内容。 在 SO 上至少还有一个其他问题 Raw Clone system call,其中 OP 似乎有类似的困难,不幸的是,接受的答案使用 libc 克隆包装器而不是系统调用。

#define _GNU_SOURCE

#include <sched.h>
#include <stdalign.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <syscall.h>
#include <signal.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>

int test(void*c)
{
    quick_exit(0);
}

int main(void)
{
    alignas (16) unsigned char stack[4096] = {0};
    printf("Top of stack %p\n", stack+sizeof(stack));
    printf("Top of stack minus 16 %p\n", stack+sizeof(stack)-16);
    pid_t pid = clone(test, stack+sizeof(stack), CLONE_VM|SIGCHLD, 0, 0, 0, 0);

    wait(NULL);

    memset(stack, 0, sizeof stack);

    pid = syscall(SYS_clone, CLONE_VM|SIGCHLD, stack+sizeof(stack)-16);
    if (pid == 0)
        quick_exit(0);
wait(NULL);
    quick_exit(0);
}

Strace 输出:

clockley@ubuntu:~$ strace ./a.out 
execve("./a.out", ["./a.out"], [/* 57 vars */]) = 0
brk(NULL)                               = 0x55b1e58ee000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f70303a0000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/tls/x86_64", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/tls", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/x86_64", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
open("/opt/google/chrome/lib/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/lib/tls/x86_64", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/lib/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/lib/tls", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/lib/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/lib/x86_64", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/opt/google/chrome/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/opt/google/chrome/lib", 0x7ffcc0e2e400) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=171231, ...}) = 0
mmap(NULL, 171231, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7030376000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\5\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1856752, ...}) = 0
mmap(NULL, 3959200, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f702fdb7000
mprotect(0x7f702ff74000, 2097152, PROT_NONE) = 0
mmap(0x7f7030174000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f7030174000
mmap(0x7f703017a000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f703017a000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7030374000
arch_prctl(ARCH_SET_FS, 0x7f7030374700) = 0
mprotect(0x7f7030174000, 16384, PROT_READ) = 0
mprotect(0x55b1e46da000, 4096, PROT_READ) = 0
mprotect(0x7f70303a3000, 4096, PROT_READ) = 0
munmap(0x7f7030376000, 171231)          = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 8), ...}) = 0
brk(NULL)                               = 0x55b1e58ee000
brk(0x55b1e590f000)                     = 0x55b1e590f000
write(1, "Top of stack 0x7ffcc0e2ecd0\n", 28Top of stack 0x7ffcc0e2ecd0
) = 28
write(1, "Top of stack minus 16 0x7ffcc0e2"..., 37Top of stack minus 16 0x7ffcc0e2ecc0
) = 37
clone(child_stack=0x7ffcc0e2ecc0, flags=CLONE_VM|SIGCHLD) = 122458
wait4(-1, NULL, 0, NULL)                = 122458
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=122458, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
clone(child_stack=0x7ffcc0e2ecc0, flags=CLONE_VM|SIGCHLD) = 122459
exit_group(0 <unfinished ...>
+++ killed by SIGSEGV +++
Segmentation fault (core dumped)

【问题讨论】:

    标签: c linux glibc strace


    【解决方案1】:

    syscall没有clone的特殊知识。这意味着当函数试图在新创建的线程中返回时,它会从切换的堆栈中读取返回地址,该地址为零。如果您将非零位模式写入堆栈并删除CLONE_VM,则这一点会更加明显,这样子级就不会破坏父级。

    【讨论】:

    • 有没有办法告诉编译器不要存储返回值?我尝试强制转换为 void,但没有成功。
    • 这不是关于返回,而是关于syscall 函数本身存储返回地址 的位置。如果不修改syscall 函数实现,就无法改变这一点,并且不完全清楚更改应该是什么样子(可能类似于vfork 中使用的内容)。改进更高级别的clone 接口可能会更好。
    猜你喜欢
    • 1970-01-01
    • 2013-07-30
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2019-01-30
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    相关资源
    最近更新 更多