2013-09-16 13 views
5

Ich habe versucht, mprotect gegen Lesen zuerst zu verwenden und dann zu schreiben.Verhalten von PROT_READ und PROT_WRITE mit mprotect

Ist hier mein 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; 
} 

Unter Linux sind hier die Ergebnisse:

Hier ist der Ausgang für PROT_WRITE ist:

$ ./main 
a = 42 
a = 24 

und für PROT_READ

$ ./main 
a = 42 
Segmentation fault 

Unter Mac OS X 10.7:

Hier ist die Ausgabe für PROT_WRITE:

$ ./main 
a = 42 
a = 24 

und für PROT_READ

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

Bisher Ich verstehe, dass OSX/Linux Verhalten anders sein könnte, aber ich verstehe nicht, warum PROT_WRITE das Programm beim Lesen des Wertes mit printf nicht abstürzt.

Kann jemand diesen Teil erklären?

+0

Warum sollten Sie erwarten, dass 'PROT_WRITE' zum Absturz kommt? – Art

+0

weil mit 'PROT_WRITE'-Flag nur Speicher AFAIK unlesbar sein soll. Wenn Sie einen RW-Zugriff wünschen, benötigen Sie 'PROT_WRITE | PROT_READ' flag – Mali

+0

@Mali right, das wäre als Frage sinnvoll wenn er beim Einlesen im Argument zum ersten printf einen Absturz erwarten würde, nicht beim Überschreiben des Wertes mit '* a = 24'. Jedenfalls habe ich versucht, das alles in meiner Antwort zu beschreiben. – Art

Antwort

7

Es gibt zwei Dinge, die Sie beobachten:

  1. mprotect wurde nicht mit Heap-Seiten verwendet werden, entworfen. Linux und OS X haben eine etwas andere Handhabung des Heaps (bedenken Sie, dass OS X die Mach VM verwendet). OS X mag es nicht, wenn seine Heap-Seiten manipuliert werden. Dies ist eine Einschränkung des MMU (x86 in meinem Fall)

    können Sie identisches Verhalten erhalten auf beiden OSes, wenn Sie Ihre Seite über mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 
    if (a == MAP_FAILED) 
        perror("mmap"); 
    
  2. zuordnen. Die MMU in x86 unterstützt schreibbare, aber nicht lesbare Seiten nicht. Daher

    mprotect(a, pagesize, PROT_WRITE) 
    

    tut nichts.

    während
    mprotect(a, pagesize, PROT_READ) 
    

    entfernt Schreib priveledges und Sie eine SIGSEGV erhalten wie erwartet.

Auch obwohl es kein Problem scheint hier zu sein, sollten Sie entweder Ihren Code mit -O0 kompilieren oder a-volatile int * setzen alle Compiler-Optimierungen zu vermeiden.

+0

1. Woher kommt die Erinnerung an "mmap", wenn nicht vom Heap? 2. Ich habe das herausgefunden, aber ich habe keine Quellen oder Hinweise dazu gefunden. Das möchte ich lösen! Danke trotzdem. – Aif

+2

Der Speicher auf "mmap" stammt aus einem freien Bereich in Ihrer VM. Es ist anders in dem Sinne, dass der Heap von einer User-Space-Bibliothek "malloc" verwaltet wird (die wiederum "mmap" nennt) und die Zuweisung in Byte-Blöcken erlaubt und die VM vom Kernel verwaltet wird und nur Zuweisungen in Chunks zulässt von Seiten. –

+0

2. Ich habe gerade das MMU-Design auf x86 durchgegangen: Die Access-Bits sind nicht als 'rwx' konzipiert, sondern als" no access "," write protected "und" executable ". Es gibt keine Möglichkeit, eine Seite physisch auf der Hardware zu schreiben, aber nicht zu lesen. –

1

Die meisten Betriebssysteme und/oder CPU-Architekturen machen automatisch etwas lesbar, wenn es beschreibbar ist, so dass PROT_WRITE meistens auch PROT_READ bedeutet. Es ist einfach nicht möglich, etwas schreibbar zu machen, ohne es lesbar zu machen.Die Gründe können spekuliert werden, entweder lohnt es sich nicht, ein zusätzliches Lesbarkeitsbit in der MMU und den Caches zu erstellen, oder wie es bei einigen früheren Architekturen war, müssen Sie die MMU tatsächlich in einen Cache lesen, bevor Sie schreiben können, Wenn man etwas unlesbar macht, wird es automatisch unlesbar.

Auch ist es wahrscheinlich, dass printf versucht, aus dem Speicher, die Sie beschädigt mit mprotect zu reservieren. Sie möchten eine vollständige Seite von libc zuweisen, wenn Sie den Schutz ändern, sonst ändern Sie den Schutz einer Seite, die Sie nicht vollständig besitzen, und libc erwartet nicht, dass sie geschützt ist. Auf Ihrem MacOS-Test mit PROT_READ passiert das. printf reserviert einige interne Strukturen, versucht auf sie zuzugreifen und stürzt ab, wenn sie schreibgeschützt sind.

+0

'printf/stdout' ist line gepuffert, also ist er gut, solange er am Ende jeder Ausgabe einen Zeilenumbruch setzt. Das Drucken auf "stderr" ist möglicherweise eine bessere Idee. –

+0

Das habe ich auch. Aber MacOS scheint nicht zuzustimmen. 'printf' auf MacOS flushed nicht, wenn nach dem ersten Anruf abstürzt. – Art

+0

Mein OS X 10.7 tut mir einmal die Seite durch 'mmap'. Selbst wenn ich es in 'stderr' ändere, stürzt es immer noch vor dem ersten Ausdruck auf' posix_memalign/mprotect (PROT_READ) 'ab. –

Verwandte Themen