Reserve a virtual address range in dynamic linker - linux

Is there a way to reserve a particular range of virtual address space in a process memory map to stop ld.so (dynamic linker) from loading any shared objects into that range. Something like a system wide configuration option that reserves a particular range.
I want to be able to map a region of shared memory into exactly the same virtual address space in several processes so that my pointers in my data-structures will still work. I know I could redesign to use offsets instead of pointers but I don't want to do that.

You can do this by creating a simple shared object and running it via LD_PRELOAD. Compile the following code:
#include <sys/mman.h> // for mmap, munmap, and related constants
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
void my_program_init() __attribute__((constructor));
void *const address = ((void*)0x10000000);
const int size = 0x1000;
void my_program_init() {
printf("Hello from my_program_init!\n");
int fd = shm_open("/mysharedmem", O_CREAT | O_RDWR, 0666);
if (fd == -1) {
printf("shm_open\n");
return;
}
if (ftruncate(fd, size) == -1) {
printf("ftruncate\n");
return;
}
void* shared_mem = mmap(address, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if (shared_mem == MAP_FAILED) {
printf("mmap\n");
return;
}
return;
}
with the following options:
gcc -shared -fPIC -o libmylib.so myso.c
Then you can run your program like this:
LD_PRELOAD=./libmylib.so ./your_prog
The so is then loaded before any runtime linking happens in your program. The function in the so tagged as a constructor runs immediately and uses mmap to reserve the memory you want for your shared block.
You can see this working with the following example program:
#include <sys/mman.h>
#include <string.h>
#include <stdio.h>
int main() {
char *data = (char*)0x10000000;
const char *message = "Hello, world!\n";
memcpy(data, message, strlen(message));
printf("Wrote %ld bytes to memory at address %p %s\n", strlen(message), data, data);
return 0;
}
If you run this without the LD_PRELOAD it will segfault, but if you include the preload the shared block of memory is available as expected.
$ LD_PRELOAD=./libmylib.so ./a.out
Hello from my_program_init!
Wrote 14 bytes to memory at address 0x10000000 Hello, world!
You can construct your own tests to validate that the memory block is actually shared but the easiest check is to recompile the test program again without the memcpy and see that the string is still there from the first run of the program.

Related

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.
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.

Get size of mmaped shared memory

I'm debugging some odd fails with reading from shared memory, and for debugging purposes I'd like to put some prints that show me currently mmaped size. So, for example, I have this code:
#include <cstdio>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <utility>
std::pair<int, void*> make_shm() {
const int shm = shm_open("foo", O_RDONLY | O_CREAT, S_IRWXO | S_IRWXG | S_IRWXU);
if (shm == -1) {
perror("connect_to_shm: shm_open failed");
return {-1, nullptr};
}
void* shm_ptr = mmap(nullptr, 15360000, PROT_READ, MAP_SHARED, shm, 0);
if (shm_ptr == MAP_FAILED) {
perror("connect_to_shm: mmap failed");
close(shm);
return {-1, nullptr};
}
return {shm, shm_ptr};
}
int main() {
auto [shm, shm_ptr] = make_shm();
// how do I print the mmapped size?
}
How do I derive the 15360000 size from the shm and shm_ptr?
I tried searching, but all results I'm getting for "get size of shared memory" are either about memory limits, or about sizes of pages. The closest I found was this question, but it's for Solaris and unanswered anyway.

pthread_create allocates a lot of memory in Linux?

Here's a simple example
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
void* run(void*)
{
while (true)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::vector<pthread_t> workers(192);
for (unsigned i = 0; i < workers.size(); ++i)
pthread_create(&workers[i], nullptr, &run, nullptr);
pthread_join(workers.back(), nullptr);
}
top shows 1'889'356 KiB VIRT! I know this isn't resident memory, but still, this is huge amount of memory for a single thread creation.
Is it really so memory-expensive (8MiB in this case) to create a thread? Is this configurable?
Or, maybe and most probably, I have some misunderstanding what virtual memory is?
Details:
I double quadruple-checked the memory usage, using:
generated a core dump of the running exe, it's also 1.6GB;
valgrind --tool=massif also confirms this size;
pmap -x <pid> also confirms the size.
As this size matches the max size of a stack (also confirmed by /proc/<pid>/limits), I tried to make the max size of the stack smaller. Tried with 1 MiB, but this didn't change anything.
Please, put aside the creation and usage of 192 threads, it has a reason behind it.
Sorry for the mixed C and C++ - initially tried with std::thread and the results are the same.
pthread_attr_setstacksize() function is available to set stack size.
This function have to be used with an thread attribute object.
The thread attribute object has to be passed as 2nd argument of pthread_create().
#include <iostream>
#include <thread>
#include <vector>
#include <chrono>
void* run(void*)
{
while (true)
std::this_thread::sleep_for(std::chrono::seconds(1));
}
int main()
{
std::vector<pthread_t> workers(192);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 16384);
for (unsigned i = 0; i < workers.size(); ++i)
pthread_create(&workers[i], &attr, &run, nullptr);
pthread_join(workers.back(), nullptr);
}

shmget previously created memory segment fails

I'm trying to learn the IPC UNIX APIs, specifically shared memory. I have created this small program that tries to either access the shared memory segment or create one.
This is what I do:
gcc -Wall -Wextra *.c
# in one terminal
./a.out
# in another
/a.out
The shared.mem file you can see in the source IS present in the same directory from which I launch the executable.
However, it seems like I'm never actually accessing a previously created shared memory segment (error is "No such file or directory"). I always create a new one - as seen via the ipcs command line, even though the IPC key stays the same.
What am I doing wrong ?
Below is the code I used, for reference. It compiles at least on Linux.
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define exit_error(what) exit_error_func(what, __FILE__, __LINE__)
#define SHM_SIZE (64)
#define UNUSED(x) (void)(x)
void *shm_addr = NULL;
void exit_error_func(const char *what, const char *file, int line)
{
fprintf(stderr, "Error in %s at line %d: %s. Reason: %s.\n", file, line, what, strerror(errno));
exit(1);
}
void sigint_handler(int sig)
{
shmdt(shm_addr);
UNUSED(sig);
}
int main(void)
{
key_t ipc_key;
int shm_id;
if ((ipc_key = ftok("shared.mem", 1)) == -1)
exit_error("could not get IPC key");
printf("IPC key is %d\n", ipc_key);
if ((shm_id = shmget(ipc_key, SHM_SIZE, 0600)) == -1)
{
printf("could not get SHM id, trying to create one now\n");
if ((shm_id = shmget(ipc_key, SHM_SIZE, IPC_EXCL | IPC_CREAT | 0600)) == -1)
exit_error("could not create or get shared memory segment");
else
printf("created SHM id\n");
}
else
printf("got already existing SHM id\n");
printf("SHM id is %d\n", shm_id);
if ((shm_addr = shmat(shm_id, NULL, 0)) == (void *)-1)
exit_error("could not attach to segment");
signal(SIGINT, sigint_handler);
if (shmctl(shm_id, IPC_RMID, NULL) == -1)
exit_error("could not flag shared memory for deletion");
printf("SHM flagged for deletion\n");
while (1)
sleep(1);
return (0);
}
It appears that it is not possible to shmget a shared memory segment that is flagged for deletion. Therefore, the shared memory segment must be marked for deletion once no process needs to shmget it anymore.
Disclaimer: I am no UNIX expert. Although the proposed solution works for me, I am still learning and cannot guarantee accuracy of the information given here.

linux ptrace() get function information

i want to catch information from user defined function using ptrace() calls.
but function address is not stable(because ASLR).
how can i get another program's function information like gdb programmatically?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <dlfcn.h>
#include <errno.h>
void error(char *msg)
{
perror(msg);
exit(-1);
}
int main(int argc, char **argv)
{
long ret = 0;
void *handle;
pid_t pid = 0;
struct user_regs_struct regs;
int *hackme_addr = 0;
pid = atoi(argv[1]);
ret = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
if(ret<0)
{
error("ptrace() error");
}
ret = waitpid(pid, NULL, WUNTRACED);
if(ret<0)
{
error("waitpid ()");
}
ret = ptrace(PTRACE_GETREGS, pid, NULL, &regs);
if(ret<0)
{
error("GETREGS error");
}
printf("EIP : 0x%x\n", (int)regs.eip);
ptrace(PTRACE_DETACH, pid, NULL, NULL);
return 0;
}
ptrace is a bit ugly, but it can be useful.
Here's a ptrace example program; it's used to make I/O-related system calls pause.
http://stromberg.dnsalias.org/~strombrg/slowdown/
You could of course also study gdb, but ISTR it's pretty huge.
You might also check out strace and ltrace, perhaps especially ltrace since it lists symbols.
HTH
You probably want to call a function that resides in a specific executable (probably, a shared object). So, first, you will have to find the base address this executable is mapped on using
/proc/pid/maps
After that, you need to find the local offset of the function you are interested in, and you can do this in two ways:
Understand the ELF file format (Linux native executable format), and searching the desired function using the mapped file (This requires some specialty)
Using a ready to use elfparser (probably readelf tool) to get the function offset under the executable. Note that you will have to figure out the real local offset since this tool usually gives you the address as if the executable was mapped to a specific address

Resources