Katoptriss
Katoptriss

Reputation: 107

Is it possible to update the flags of an existing memory mapping?

I am writing a small program in x64 assembly that will spawn children, all sharing their memory mappings so they can modify each other's code. For this, since the argument CLONE_VM of sys_clone seems to place the program into undefined behaviour, I plan to use mmap's MAP_SHARED argument.

However, I would also need the children to modify the code of the father. One option is to also allocate a MAP_SHARED mapping and give it to the father, but I'd like to avoid this if possible (only for elegance reasons).

Since the base mapping (the 0x400000 one on 64-bits Linux) of the program will not have the MAP_SHARED flag by default, is it possible to update it using a syscall to set this flag? munmap then mmap will not do and cause a SIGSEGV, and mprotect can only change the RWX permissions.

Upvotes: 3

Views: 524

Answers (1)

You can't change whether an existing mapping is private or shared, but you can map a new shared mapping over an existing private mapping. You can even do so in C, like this:

#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main(void) {
    FILE *stream = fopen("/proc/self/maps", "rb");
    if(!stream) {
        perror("fopen");
        return 1;
    }
    char *text_start, *text_end;
    do {
        if(fscanf(stream, " %p-%p%*[^\n]", &text_start, &text_end) != 2) {
            perror("scanf");
            return 1;
        }
    } while(!(text_start <= main && main < text_end));
    if(fclose(stream)) {
        perror("fclose");
        return 1;
    }
    size_t text_len = text_end - text_start;
    char *mem = mmap(NULL, text_len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    if(mem == MAP_FAILED) {
        perror("mmap");
        return 1;
    }
    memcpy(mem, text_start, text_len);
    __builtin___clear_cache(mem, mem + text_len);
    if(mremap(mem, text_len, text_len, MREMAP_MAYMOVE|MREMAP_FIXED, text_start) == MAP_FAILED) {
        perror("mremap");
        return 1;
    }
    /* you can check /proc/PID/maps now to see the new mapping */
    getchar();
}

As a bonus, this program supports ASLR and doesn't require that the text section start at 0x400000.

Upvotes: 4

Related Questions