2013-09-16 7 views
5

Próbowałem użyć mprotect do czytania najpierw, a następnie pisania.Zachowanie PROT_READ i PROT_WRITE z mprotect

Jest tu mój kod

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

Pod Linuksem oto wyniki:

Oto wyjście PROT_WRITE:

$ ./main 
a = 42 
a = 24 

i PROT_READ

$ ./main 
a = 42 
Segmentation fault 

W systemie Mac OS X 10.7:

Oto wyjście PROT_WRITE:

$ ./main 
a = 42 
a = 24 

i PROT_READ

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

Dotychczas Rozumiem, że zachowanie OSX/Linux może być różny, ale nie rozumiem, dlaczego PROT_WRITE nie powoduje awarii programu podczas odczytu wartości za pomocą printf.

Czy ktoś może wyjaśnić tę część?

+0

Dlaczego miałbyś oczekiwać, że 'PROT_WRITE' ulegnie awarii? – Art

+0

ponieważ tylko z flagą 'PROT_WRITE', pamięć ma być nieczytelna AFAIK. Jeśli chcesz uzyskać dostęp rw, potrzebujesz 'PROT_WRITE | PROT_READ' flag – Mali

+0

@ Mali right, miałoby sens jako pytanie, czy spodziewał się awarii podczas czytania w argumencie do pierwszego printf, a nie podczas nadpisywania wartości przez '* a = 24'. W każdym razie próbowałem to wszystko ująć w mojej odpowiedzi. – Art

Odpowiedz

7

Są dwie rzeczy, które obserwujesz:

  1. mprotect nie został zaprojektowany do użytku z stron sterty. Linux i OS X mają nieco inną obsługę sterty (pamiętaj, że OS X używa Mach VM). System OS X nie lubi, aby manipulować stronami sterty.

    Można uzyskać identyczne zachowanie na obu systemów operacyjnych, jeśli przeznaczyć swoją stronę poprzez mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 
    if (a == MAP_FAILED) 
        perror("mmap"); 
    
  2. To ograniczenie swojej MMU (x86 w moim przypadku). MMU w x86 nie obsługuje zapisywalnych, ale nieodczytywalnych stron. Tak więc ustawienie nie powoduje żadnych zmian. podczas gdy

    usunięto priveledges i otrzymujesz SIGSEGV zgodnie z oczekiwaniami.

Również chociaż nie wydaje się być problemem tutaj, należy albo skompilować kod z -O0 lub ustawić a do volatile int * aby uniknąć optymalizacje kompilatora.

+0

1. skąd pochodzi pamięć 'mmap', jeśli nie pochodzi z sterty? 2. Rozgryzłem to, ale nie znalazłem żadnych źródeł ani wskazówek na ten temat. Właśnie to chciałbym rozwiązać! Dzięki mimo to. – Aif

+2

Pamięć na 'mmap' pochodzi z wolnego obszaru w VM. Jest inny w tym sensie, że sterta jest zarządzana przez bibliotekę 'malloc' przestrzeni użytkownika (która z kolei wywołuje' mmap') i pozwala na przydział w porcjach bajtów, a VM jest zarządzana przez jądro i pozwala tylko na przydziały w porcjach stron. –

+0

2. Właśnie przeszedłem projekt MMU na x86: Bity dostępu nie zostały zaprojektowane jako 'rwx', ale jako" brak dostępu "," ochrona przed zapisem "i" plik wykonywalny ". Nie ma sposobu, aby ustawić stronę "napisz, ale nie czytaj" fizycznie na sprzęcie. –

1

Większość systemów operacyjnych i/lub architektur procesorów automatycznie tworzy coś czytelnego, gdy można go zapisać, więc PROT_WRITE najczęściej oznacza także. Po prostu nie można stworzyć czegoś, co można zapisać, nie czyniąc go czytelnym.Powody mogą być spekulowane, albo nie jest warte wysiłku, aby uczynić dodatkowy bit czytelności w MMU i pamięciach podręcznych, lub jak to było w niektórych wcześniejszych architekturach, faktycznie trzeba przeczytać MMU w pamięci podręcznej, zanim będzie można napisać, więc sprawienie, że coś nieczytelnego automatycznie spowoduje, że nie będzie można go nagrać.

Ponadto prawdopodobnie printf próbuje przydzielić z pamięci uszkodzoną przez mprotect. Chcesz przydzielić pełną stronę z biblioteki libc podczas zmiany jej ochrony, w przeciwnym razie zmienisz ochronę strony, której nie jesteś w pełni właścicielem, a biblioteka libc nie oczekuje, że będzie chroniona. W testach MacOS z PROT_READ tak właśnie się dzieje. printf przydziela niektóre struktury wewnętrzne, próbuje uzyskać do nich dostęp i ulega awarii, gdy są one tylko do odczytu.

+0

'printf/stdout' jest buforowany liniowo, więc jest dobry tak długo, jak ustawia linię podziału na końcu każdego wyjścia. Nadal drukowanie na 'stderr' może być lepszym pomysłem. –

+0

To też ja też. Ale MacOS się nie zgadza. 'printf' na MacOS nie spłukuje się po awarii po pierwszym połączeniu. – Art

+0

Mój OS X 10.7 raz przydziela stronę poprzez 'mmap'. Nawet jeśli zmienię go na 'stderr' to nadal ulega awarii przed pierwszym wydrukiem na' posix_memalign/mprotect (PROT_READ) '. –

Powiązane problemy