Reading a child process's /proc/pid/mem file from the parent

Posted by Amittai Aviram on Stack Overflow See other posts from Stack Overflow or by Amittai Aviram
Published on 2010-12-30T19:25:06Z Indexed on 2010/12/31 0:54 UTC
Read the original article Hit count: 327

Filed under:
|
|
|
|

In the program below, I am trying to cause the following to happen:

  1. Process A assigns a value to a stack variable a.
  2. Process A (parent) creates process B (child) with PID child_pid.
  3. Process B calls function func1, passing a pointer to a.
  4. Process B changes the value of variable a through the pointer.
  5. Process B opens its /proc/self/mem file, seeks to the page containing a, and prints the new value of a.
  6. Process A (at the same time) opens /proc/child_pid/mem, seeks to the right page, and prints the new value of a.

The problem is that, in step 6, the parent only sees the old value of a in /proc/child_pid/mem, while the child can indeed see the new value in its /proc/self/mem. Why is this the case? Is there any way that I can get the parent to to see the child's changes to its address space through the /proc filesystem?

#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;
}

This program produces the output below. Notice that the value of a (boldfaced) differs between parent's and child's reading of the /proc/child_pid/mem file.

In main: address of arg.a: 0x7ffffe1964f0
Stack_start: 7ffffe196000
Address of arg_ptr->a: 0x7ffffe1964f0
PARENT (pid 20376, child pid 20377)
PARENT: child_offset: 4f0
CHILD (pid 20377, parent pid 20376).
func1: old value: 42
func1: address: 0x7ffffe1964f0
func1: new value: 53
PARENT: message from child: 1
CHILD: child_mem_fd: 4
PARENT: child_path: /proc/20377/mem
CHILD: new value 53
PARENT: child_mem_fd: 7
PARENT: new value 42
ENDING CHILD PROCESS.

© Stack Overflow or respective owner

Related posts about linux

Related posts about ipc