Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a thread-based mprotect?

mprotect() is used to protect memory pages, for example, making pages read-only. It sets this protection for the whole process, that is, if a page is read-only, no thread can write to that page. Is there a way to protect pages in different ways for different threads? For example, 1 thread can write to page P, and all other threads in my program can only read from P.

like image 546
danyhow Avatar asked Aug 23 '13 08:08

danyhow


2 Answers

If you create a thread using CLONE_VM flag in the "clone" system call (this is what you would normally call a thread) then the MMU settings are the same as for the parent thread.

This means that write access is possible for both threads.

If you do not use CLONE_VM flags then the both threads do not have shared memory at all!

(pthread_create() sets the CLONE_VM flag internally).

It would be possible to do what you want - however it would be very difficult:

Allocate all memory blocks using shared memory functions (e.g. shmget()) instead of standard functions (e.g. malloc()).

If a new thread is created use "clone()" directly instead of "pthread_create()" with the CLONE_VM flag not set.

The shared memory is shared between the threads and the threads created by "normal" memory allocation functions (e.g. malloc()) is not shared between the threads. The same is true for mmap() mapped memory.

When a new thread is created such memory blocks (created by malloc or mmap) are copied so that both threads have their own copy of this memory block at the same address. If one thread writes to this address then the other thread will not see the change.

Allocating more "shared" memory is rather tricky. It is easy if the memory only should be shared between the allocating thread and child threads that are not created, yet. it is difficult to share memory between already-running threads or between threads that are (indirect) children of different already-running threads.

The threads do not have shared stack memory so they cannot access each other's stack.

Global and "static" variables are not shared by default - to make them "shared" between the threads some tricky programming is required.

like image 163
Martin Rosenau Avatar answered Sep 28 '22 05:09

Martin Rosenau


With newer Intel CPUs you can use Memory Protection Keys [1] for different access settings per thread within a process. On Linux, run lscpu and check for the pku and ospke flags.

The example on the man page [2] is a bit outdated in the sense that the corresponding system calls do not need to be invoked manually anymore. Instead, glibc provides the following API calls:

  • pkey_alloc() to allocate a new key (16 are available)
  • pkey_set() to set the permission for a given key
  • pkey_mprotect() to apply the key to a given memory region
  • pkey_free() to free a key

Since the register that maintains the permission bits per protection key is thread-local, different protection settings are possible per thread. Protection key settings can only further lock down the general settings and do not affect instruction fetch.

[1] https://www.kernel.org/doc/Documentation/x86/protection-keys.txt

[2] http://man7.org/linux/man-pages/man7/pkeys.7.html

like image 23
Matthias Neugschwandtner Avatar answered Sep 28 '22 05:09

Matthias Neugschwandtner