在下面的程序中,我试图导致以下情况发生:
问题在于,在第6步中,父级只会在 / proc / child_pid中看到 a 的旧值 / mem ,而孩子确实可以在 / proc / self / mem 中看到新值。为什么会这样?有什么方法可以让父母通过 / proc 文件系统查看孩子对地址空间的更改?
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#define PAGE_SIZE 0x1000
#define LOG_PAGE_SIZE 0xc
#define PAGE_ROUND_DOWN(v) ((v) & (~(PAGE_SIZE - 1)))
#define PAGE_ROUND_UP(v) (((v) + PAGE_SIZE - 1) & (~(PAGE_SIZE - 1)))
#define OFFSET_IN_PAGE(v) ((v) & (PAGE_SIZE - 1))
# if defined ARCH && ARCH == 32
#define BP "ebp"
#define SP "esp"
#else
#define BP "rbp"
#define SP "rsp"
#endif
typedef struct arg_t {
int a;
} arg_t;
void func1(void * data) {
arg_t * arg_ptr = (arg_t *)data;
printf("func1: old value: %d\n", arg_ptr->a);
arg_ptr->a = 53;
printf("func1: address: %p\n", &arg_ptr->a);
printf("func1: new value: %d\n", arg_ptr->a);
}
void expore_proc_mem(void (*fn)(void *), void * data) {
off_t frame_pointer, stack_start;
char buffer[PAGE_SIZE];
const char * path = "/proc/self/mem";
int child_pid, status;
int parent_to_child[2];
int child_to_parent[2];
arg_t * arg_ptr;
off_t child_offset;
asm volatile ("mov %%"BP", %0" : "=m" (frame_pointer));
stack_start = PAGE_ROUND_DOWN(frame_pointer);
printf("Stack_start: %lx\n",
(unsigned long)stack_start);
arg_ptr = (arg_t *)data;
child_offset =
OFFSET_IN_PAGE((off_t)&arg_ptr->a);
printf("Address of arg_ptr->a: %p\n",
&arg_ptr->a);
pipe(parent_to_child);
pipe(child_to_parent);
bool msg;
int child_mem_fd;
char child_path[0x20];
child_pid = fork();
if (child_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (!child_pid) {
close(child_to_parent[0]);
close(parent_to_child[1]);
printf("CHILD (pid %d, parent pid %d).\n",
getpid(), getppid());
fn(data);
msg = true;
write(child_to_parent[1], &msg, 1);
child_mem_fd = open("/proc/self/mem", O_RDONLY);
if (child_mem_fd == -1) {
perror("open (child)");
exit(EXIT_FAILURE);
}
printf("CHILD: child_mem_fd: %d\n", child_mem_fd);
if (lseek(child_mem_fd, stack_start, SEEK_SET) == (off_t)-1) {
perror("lseek");
exit(EXIT_FAILURE);
}
if (read(child_mem_fd, buffer, sizeof(buffer))
!= sizeof(buffer)) {
perror("read");
exit(EXIT_FAILURE);
}
printf("CHILD: new value %d\n",
*(int *)(buffer + child_offset));
read(parent_to_child[0], &msg, 1);
exit(EXIT_SUCCESS);
}
else {
printf("PARENT (pid %d, child pid %d)\n",
getpid(), child_pid);
printf("PARENT: child_offset: %lx\n",
child_offset);
read(child_to_parent[0], &msg, 1);
printf("PARENT: message from child: %d\n", msg);
snprintf(child_path, 0x20, "/proc/%d/mem", child_pid);
printf("PARENT: child_path: %s\n", child_path);
child_mem_fd = open(path, O_RDONLY);
if (child_mem_fd == -1) {
perror("open (child)");
exit(EXIT_FAILURE);
}
printf("PARENT: child_mem_fd: %d\n", child_mem_fd);
if (lseek(child_mem_fd, stack_start, SEEK_SET) == (off_t)-1) {
perror("lseek");
exit(EXIT_FAILURE);
}
if (read(child_mem_fd, buffer, sizeof(buffer))
!= sizeof(buffer)) {
perror("read");
exit(EXIT_FAILURE);
}
printf("PARENT: new value %d\n",
*(int *)(buffer + child_offset));
close(child_mem_fd);
printf("ENDING CHILD PROCESS.\n");
write(parent_to_child[1], &msg, 1);
if (waitpid(child_pid, &status, 0) == -1) {
perror("waitpid");
exit(EXIT_FAILURE);
}
}
}
int main(void) {
arg_t arg;
arg.a = 42;
printf("In main: address of arg.a: %p\n", &arg.a);
explore_proc_mem(&func1, &arg.a);
return EXIT_SUCCESS;
}
该程序产生以下输出。请注意, a (粗体)的值在父母和孩子读取 / proc / child_pid / mem 文件。
在main中:arg.a的地址:0x7ffffe1964f0
Stack_start:7ffffe196000
地址arg_ptr-&gt; a:0x7ffffe1964f0
父母(pid 20376,儿童pid 20377)
父母:child_offset:4f0
儿童(pid 20377,父母pid 20376)
func1:旧值:42
func1:地址:0x7ffffe1964f0
func1:新值:53
父母:来自孩子的信息:1
孩子:child_mem_fd:4
父母:child_path:/ proc / 20377 / mem
儿童:新值 53
父母:child_mem_fd:7
父母:新值 42
结束儿童过程。
答案 0 :(得分:3)
这段代码中有一个愚蠢的错误:
const char * path = "/proc/self/mem";
...
snprintf(child_path, 0x20, "/proc/%d/mem", child_pid);
printf("PARENT: child_path: %s\n", child_path);
child_mem_fd = open(path, O_RDONLY);
所以你总是在这里阅读父母的记忆。但是在改变之后,我得到了:
CHILD: child_mem_fd: 4
CHILD: new value 53
read (parent): No such process
我不知道为什么会发生这种情况 - 也许/proc
刷新条目太慢了? (它来自父级中的perror("read")
- 必须添加注释以查看哪个失败了)但这似乎很奇怪,因为seek
工作 - 以及open
本身。
这个问题似乎也不新鲜:http://lkml.indiana.edu/hypermail/linux/kernel/0007.1/0939.html(ESRCH是“没有这样的过程”)
实际上更好的链接是:http://www.webservertalk.com/archive242-2004-7-295131.html - 标记进程pthread-attach-safe存在问题。你可以在那里找到艾伦考克斯派人到太阳设计师...对我来说,这里拼写“这里是龙”,如果你不在睡眠中破解内核,它就无法解决:(
也许这足以让你检查gdb在这种情况下做了什么并复制它? (可能只是通过ptrace(PTRACE_PEEKDATA,...)
)
答案 1 :(得分:2)
解决方案是使用 ptrace 将父级与子级同步。即使我已经在父母和孩子之间进行交流(并且 ptrace 的手册页说它导致两个进程的行为就像它们是父母和孩子一样),即使孩子阻止读取调用,孩子显然没有“停止”足够Linux让父母读取孩子的 / proc / child_pid / mem 文件。但是,如果父级首先使用PTRACE_ATTACH调用 ptrace (在通过管道接收消息之后),则它可以打开文件 - 并获取正确的内容!然后,父母再次使用PTRACE_DETACH调用 ptrace ,然后再将消息发送给孩子终止。