Reputation: 107
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
Reputation: 48582
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