Welcome to the Linux Foundation Forum!

There seems to be memory leak when reading /proc/pid/mem from another process

I tried to read memory content of process from another process. However, VmRss of the target process was rapidly increased while reading its memory. Why this is happening?

Please check the following test code I used. This program fork child process and read its memory. the child process do nothing, but VmRss of the child process will be increased while the parent process reading child process's memory.

The test code is the following.

#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <err.h>
#include <cerrno>
#include <cstring>
#include <fcntl.h>

#include <vector>
#include <utility>

const int RD_SIZE = 1 * 1024 * 1024;

void read_stat(char *path) {
    int fd = open(path, O_RDONLY);
    char *buf = (char*)malloc(sizeof(char) * 128);
    size_t statm_len = read(fd, buf, 128);
    int err = errno;
    if (statm_len == -1) {
        printf("failed to read statm at %d: %s\n", err, strerror(err));
        exit(EXIT_FAILURE);
    } else {
        printf("%s\n", buf);
    }
    free(buf);
    close(fd);
}

// 41e53000-41e58000 rw-p 00000000 00:00 0 xxxxxx
std::vector<std::pair<unsigned long, unsigned long> > read_maps(char *path) {
    FILE *f = fopen(path, "r");
    char *buf = (char*)malloc(sizeof(char) * 16);
    int len = 0;
    int read_phase = 0;
    unsigned long from;
    unsigned long to;
    std::vector<std::pair<unsigned long, unsigned long> > res;
    while (true) {
        int v = fgetc(f);
        if (v == EOF)   break;

        if (read_phase == 0) {
            if (v == '-') {
                from = strtol(buf, NULL, 16);
                len = 0;
                buf[0] = '\0';
                read_phase = 1;
            } else {
                buf[len++] = v;
                buf[len] = '\0';
            }
        } else if (read_phase == 1) {
            if (v == ' ') {
                to = strtol(buf, NULL, 16);
                len = 0;
                buf[0] = '\0';
                read_phase = 2;
            } else {
                buf[len++] = v;
                buf[len] = '\0';
            }
        } else if (read_phase == 2) {
            if (v == 'r') {
                unsigned long cur_pos = from;
                while (cur_pos < to) {
                    unsigned long next_pos = cur_pos + RD_SIZE;
                    if (next_pos > to)  next_pos = to;
                    unsigned long length = next_pos - cur_pos;
                    res.push_back(std::make_pair(cur_pos, length));
                    cur_pos = next_pos;
                }
            } else if (v == '-') {
                // nothing to do
            } else {
                printf("something wrong in parse\n");
                exit(EXIT_FAILURE);
            }
            read_phase = 3;
        } else {
            if (v == '\n') {
                read_phase = 0;
            }
        }
    }
    free(buf);
    fclose(f);
    return res;
}

int main(int argc, char *argv[]) {
    pid_t pid;

    pid = fork();

    if (pid == -1) {
        err(EXIT_FAILURE, "failed to fork");
    } else if (pid == 0) {
        // child process
        while (true) {
            sleep(10);
        }
        exit(EXIT_SUCCESS);
    } else {
        sleep(3);
        // parent
        printf("parent: child pid is %d\n", pid);

        // attach to the process
        int ptrace_res = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
        if (ptrace_res < 0) {
            perror("failed to attach to the child process");
            exit(EXIT_FAILURE);
        }
        sleep(1);

        char *buf = NULL;
        char *path = (char*)malloc(sizeof(char) * 128);

        // read from /proc/pid/statm
        sprintf(path, "/proc/%d/statm", pid);
        read_stat(path);

        // read from /proc/pid/maps
        sprintf(path, "/proc/%d/maps", pid);
        auto target_list = read_maps(path);

        // read from /proc/pid/mem
        sprintf(path, "/proc/%d/mem", pid);
        int fd = open(path, O_RDONLY);
        buf = (char*)malloc(sizeof(char) * RD_SIZE);

        for (auto it : target_list) {
            unsigned long from, length;
            from = it.first;
            length = it.second;
            // printf("read from %lx, length: %lu\n", from, length);
            lseek64(fd, from, SEEK_SET);
            size_t ret = read(fd, buf, RD_SIZE);
            int err = errno;
            // skip I/O error
            if (ret == -1 && err != 5) {
                printf("failed to read at %d: %s\n", err, strerror(err));
                break;
            }
        }

        ptrace(PTRACE_DETACH, pid, NULL, NULL);

        close(fd);

        // read from /proc/pid/statm
        sprintf(path, "/proc/%d/statm", pid);
        read_stat(path);
    }

    exit(EXIT_SUCCESS);
}

Categories

Upcoming Training