Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C/Linux: dual-map memory with different permissions

Tags:

c

linux

mmap

My program passes data pointers to third-party plugins with the intention that the data should be read-only, so it would be nice to prevent the plugins from writing to the data objects. Ideally there would be a segfault if a plugin attempts a write. I've heard there is some way to double-map a memory region, such that a second virtual address range points to the same physical memory pages. The second mapping would not have write permission, and the exported pointers would use this address range instead of the original (writable) one. I would prefer not to change the original memory allocations, whether they happen to use malloc or mmap or whatever. Can someone explain how to do this?

like image 497
Byron Hawkins Avatar asked Aug 20 '14 09:08

Byron Hawkins


1 Answers

It is possible to get a dual mapping, but it requires some work.

The only way I know how to create such a dual mapping is to use the mmap function call. For mmap you need some kind of file-descriptor. Fortunately Linux allows you to get a shared memory object, so no real file on a storage medium is required.

Here is a complete example that shows how to create the shared memory object, creates a read/write and read-only pointer from it and then does some basic tests:

#include <stdio.h> 
#include <fcntl.h>          
#include <unistd.h>         
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>        
#include <sys/types.h>

int main()  
{
        // Lets do this demonstration with one megabyte of memory:
        const int len = 1024*1024;

        // create shared memory object:
        int fd = shm_open("/myregion", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
        printf ("file descriptor is %d\n", fd);

        // set the size of the shared memory object:
        if (ftruncate(fd, len) == -1)
        {
            printf ("setting size failed\n");
            return 0;
        }

        // Now get two pointers. One with read-write and one with read-only.
        // These two pointers point to the same physical memory but will
        // have different virtual addresses:

        char * rw_data = mmap(0, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd,0);
        char * ro_data = mmap(0, len, PROT_READ           , MAP_SHARED, fd,0);

        printf ("rw_data is mapped to address %p\n", rw_data);
        printf ("ro_data is mapped to address %p\n", ro_data);

        // ===================
        // Simple test-bench:
        // ===================

        // try writing:
        strcpy (rw_data, "hello world!");

        if (strcmp (rw_data, "hello world!") == 0)
        {
            printf ("writing to rw_data test passed\n");
        } else {
            printf ("writing to rw_data test failed\n");
        }

        // try reading from ro_data
        if (strcmp (ro_data, "hello world!") == 0)
        {
            printf ("reading from ro_data test passed\n");
        } else {
            printf ("reading from ro_data test failed\n");
        }

        printf ("now trying to write to ro_data. This should cause a segmentation fault\n");

        // trigger the segfault
        ro_data[0] = 1;

        // if the process is still alive something didn't worked.
        printf ("writing to ro_data test failed\n");
        return 0;
}

Compile with: gcc test.c -std=c99 -lrt

For some reason I get a warning that ftruncate is not declared. No idea why. The code works well though. Example output:

file descriptor is 3
rw_data is mapped to address 0x7f1778d60000
ro_data is mapped to address 0x7f1778385000
writing to rw_data test passed
reading from ro_data test passed
now trying to write to ro_data. This should cause a segmentation fault
Segmentation fault

I've left the memory deallocation as an exercise for the reader :-)

like image 189
Nils Pipenbrinck Avatar answered Oct 22 '22 05:10

Nils Pipenbrinck