次のコードを考えてみましょう (公共の要求によりコンパイルするために再訪しました:):
#include <assert.h>
#include <signal.h>
#include <stdio.h>
#include <syscall.h>
#include <unistd.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <sys/user.h>
#include <sys/wait.h>
static FILE* logfile = 0;
int main(int argc, char * argv[], char * envp[]) {
pid_t pid = fork();
if (pid == 0) { /* child */
if (ptrace(PTRACE_TRACEME, 0, 0, 0) == -1)
assert(0 && "ptrace traceme failed");
/* signal the parent that the child is ready */
kill(getpid(), SIGSTOP);
execve("a.out", argv, envp);
} else { /* parent */
int status = 0, ret = 0;
if ((logfile = fopen("log","a+")) == 0)
assert(0 && "failed to open logfile");
struct stat logfile_stat_buf;
if (stat("log", &logfile_stat_buf) != 0)
assert(0 && "failed to stat logfile");
/* sync the parent and the child */
if (waitpid(pid, &status, __WALL | __WCLONE) < 0)
assert(0 && "waiting on child failed");
ptrace(PTRACE_SYSCALL, pid, 0, 0);
while (waitpid(pid, &status, __WALL | __WCLONE) > 0) {
/* syscall entry */
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
/* check to see if it's a mmap call
* void *mmap2(void *addr, size_t length, int prot,int flags, int fd, off_t pgoffset);
*/
printf("Child entereing a syscall %d\n", regs.orig_eax);
if (regs.orig_eax == SYS_mmap2) {
/* stat the file for the inode */
int fd = regs.edi;
struct stat stat_buf;
if ((fstat(fd, &stat_buf) == 0) && (stat_buf.st_ino == logfile_stat_buf.st_ino))
assert(0 && "child trying to mmap parent inode");
}
ptrace(PTRACE_SYSCALL, pid, 0, 0);
waitpid(pid, &status, __WALL | __WCLONE);
ptrace(PTRACE_GETREGS, pid, NULL, ®s);
/* syscall exit */
printf("Child exiting a syscall %d\n", regs.orig_eax);
ptrace(PTRACE_SYSCALL, pid, 0, 0);
}
if (fclose(logfile) != 0)
assert(0 && "failed to close logfile");
}
return 0;
}
a.out プログラムは単純な main() { return 0; です。プログラム。
このコードをコンパイルして実行すると、子プロセスが、親のfopen("log") 呼び出しによって開かれたファイルを mmap() しようとしたことがわかります。これは、失敗したアサーションによってわかります。
さらに調査したところ、これは子プロセスのロード中に発生することがわかりました。
これは 2 つの理由で奇妙です。
- fork() の後に発生したため、子供は fopen() 呼び出しにまったく気付かないはずです
- ローダーがこのファイルを mmap しようとするのはなぜですか? 実行可能ファイルでさえありません。
私は glibc で dl-load.c を見回しましたが、この種の動作を引き起こすものは何も見当たりませんでした。
何か案は?
ありがとう