Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Behaviour of PROT_READ and PROT_WRITE with mprotect

Tags:

c

mprotect

I've been trying to use mprotect against reading first, and then writing.

Is here my code

#include <sys/types.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int pagesize = sysconf(_SC_PAGE_SIZE);
    int *a;
    if (posix_memalign((void**)&a, pagesize, sizeof(int)) != 0)
        perror("memalign");

    *a = 42;
    if (mprotect(a, pagesize, PROT_WRITE) == -1) /* Resp. PROT_READ */
        perror("mprotect");

    printf("a = %d\n", *a);
    *a = 24;
    printf("a = %d\n", *a);
    free (a);
    return 0;
}

Under Linux here are the results:

Here is the output for PROT_WRITE:

$ ./main 
a = 42
a = 24

and for PROT_READ

$ ./main 
a = 42
Segmentation fault

Under Mac OS X 10.7:

Here is the output for PROT_WRITE:

$ ./main 
a = 42
a = 24

and for PROT_READ

$ ./main 
[1] 2878 bus error ./main

So far, I understand that OSX / Linux behavior might be different, but I don't understand why PROT_WRITE does not crash the program when reading the value with printf.

Can someone explain this part?

like image 513
Aif Avatar asked Sep 16 '13 13:09

Aif


1 Answers

There are two things that you are observing:

  1. mprotect was not designed to be used with heap pages. Linux and OS X have slightly different handling of the heap (remember that OS X uses the Mach VM). OS X does not like it's heap pages to be tampered with.

    You can get identical behaviour on both OSes if you allocate your page via mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
    if (a == MAP_FAILED) 
        perror("mmap");
    
  2. This is a restriction of your MMU (x86 in my case). The MMU in x86 does not support writable, but not readable pages. Thus setting

    mprotect(a, pagesize, PROT_WRITE)
    

    does nothing. while

    mprotect(a, pagesize, PROT_READ)
    

    removed write priveledges and you get a SIGSEGV as expected.

Also although it doesn't seem to be an issue here, you should either compile your code with -O0 or set a to volatile int * to avoid any compiler optimisations.

like image 116
Sergey L. Avatar answered Sep 22 '22 02:09

Sergey L.