2013-09-16 9 views
5

Ho cercato di utilizzare prima mprotect prima di leggere e poi di scrivere.Comportamento di PROT_READ e PROT_WRITE con mprotect

'qui il mio codice

#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; 
} 

Sotto Linux ecco i risultati:

Ecco l'output per PROT_WRITE:

$ ./main 
a = 42 
a = 24 

e per PROT_READ

$ ./main 
a = 42 
Segmentation fault 

In Mac OS X 10.7:

Ecco l'output per PROT_WRITE:

$ ./main 
a = 42 
a = 24 

e per PROT_READ

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

Finora, ho capito che il comportamento OSX/Linux potrebbe essere diverso, ma non capisco perché PROT_WRITE non blocchi il programma durante la lettura del valore con printf.

Qualcuno può spiegare questa parte?

+0

Perché dovresti aspettarti che "PROT_WRITE" si arresti? – Art

+0

perché con solo flag 'PROT_WRITE', la memoria dovrebbe essere illeggibile AFAIK. Se si desidera accedere rw, è necessario 'PROT_WRITE | Flag PROT_READ' – Mali

+0

@Mali a destra, che avrebbe senso come una domanda se si aspettava un crash durante la lettura dell'argomento sul primo printf, non quando sovrascriveva il valore con '* a = 24'. Ad ogni modo, ho cercato di coprire tutto questo nella mia risposta. – Art

risposta

7

ci sono due cose che si sta osservando:

  1. mprotect non è stato progettato per essere utilizzato con le pagine di heap. Linux e OS X hanno una gestione leggermente diversa dell'heap (ricorda che OS X usa Mach Mach). A OS X non piace che le pagine heap siano manomesse.

    È possibile ottenere un comportamento identico su entrambi i sistemi operativi, se si alloca la pagina tramite mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 
    if (a == MAP_FAILED) 
        perror("mmap"); 
    
  2. Si tratta di una restrizione del vostro MMU (x86 nel mio caso). La MMU in x86 non supporta pagine scrivibili, ma non leggibili. Quindi impostando

    mprotect(a, pagesize, PROT_WRITE) 
    

    non fa nulla. mentre

    mprotect(a, pagesize, PROT_READ) 
    

    rimosso scrivere privilegi e si ottiene un SIGSEGV come previsto.

Inoltre, anche se non sembra essere un problema qui, è necessario compilare il codice sia con -O0 o impostare a-volatile int * per evitare eventuali ottimizzazioni del compilatore.

+0

1. Da dove viene la memoria di 'mmap' se non da heap? 2. L'ho capito, ma non ho trovato alcuna fonte né suggerimento a riguardo. Questo è quello che mi piacerebbe risolvere! Grazie comunque. – Aif

+2

La memoria su 'mmap' proviene da un'area libera della VM. È diverso nel senso che l'heap è gestito da una libreria 'malloc' user-space (che a sua volta chiama' mmap') e consente l'allocazione in blocchi di byte e la VM è gestita dal kernel e consente solo allocazioni in blocchi di pagine. –

+0

2. Ho appena eseguito il design della MMU su x86: i bit di accesso non sono progettati come 'rwx', ma come" nessun accesso "," protetto da scrittura "e" eseguibile ". Non c'è modo di impostare una pagina "scrivi, ma non leggi" fisicamente sull'hardware. –

1

La maggior parte dei sistemi operativi e/o architetture della cpu rendono automaticamente leggibile quando è scrivibile, quindi PROT_WRITE implica spesso anche PROT_READ. Semplicemente non è possibile creare qualcosa scrivibile senza renderlo leggibile.Le ragioni possono essere ipotizzate, o non vale la pena di fare un ulteriore bit di leggibilità nella MMU e nelle cache, o come era in alcune architetture precedenti, in realtà è necessario leggere la MMU in una cache prima di poter scrivere, quindi rendere qualcosa di illeggibile lo rende automaticamente non scrivibile.

Inoltre, è probabile che printf provi ad allocare dalla memoria danneggiata con mprotect. Vuoi assegnare una pagina intera da libc quando stai cambiando la sua protezione, altrimenti cambierai la protezione di una pagina che non possiedi completamente e libc non si aspetta che sia protetto. Sul tuo test MacOS con PROT_READ questo è ciò che accade. printf assegna alcune strutture interne, tenta di accedervi e si blocca quando sono di sola lettura.

+0

'printf/stdout' ha una linea bufferizzata, quindi è buono finché imposta un'interruzione di riga alla fine di ogni output. Stampare ancora su 'stderr' potrebbe essere un'idea migliore. –

+0

Questo è quello che penso anche io. Ma MacOS non sembra essere d'accordo. 'printf' su MacOS non scarica quando si blocca dopo la prima chiamata. – Art

+0

Il mio OS X 10.7 fa una volta che alloco la pagina tramite 'mmap'. Anche se lo cambio in 'stderr', si blocca ancora prima della prima stampa su' posix_memalign/mprotect (PROT_READ) '. –