2013-09-16 35 views
5

Tôi đã cố gắng sử dụng mprotect để đọc trước, sau đó viết.Hành vi của PROT_READ và PROT_WRITE với mprotect

Là ở đây mã của tôi

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

Dưới Linux, đây là kết quả:

Đây là kết quả cho PROT_WRITE:

$ ./main 
a = 42 
a = 24 

và cho PROT_READ

$ ./main 
a = 42 
Segmentation fault 
.210

Theo Mac OS X 10.7:

Đây là kết quả cho PROT_WRITE:

$ ./main 
a = 42 
a = 24 

và cho PROT_READ

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

Cho đến nay, tôi hiểu rằng hành vi OSX/Linux có thể khác nhau, nhưng tôi không hiểu tại sao PROT_WRITE không làm hỏng chương trình khi đọc giá trị với printf.

Ai đó có thể giải thích phần này?

+0

Tại sao bạn mong đợi 'PROT_WRITE' gặp sự cố? – Art

+0

vì chỉ với cờ "PROT_WRITE', bộ nhớ được cho là không thể đọc được AFAIK. Nếu bạn muốn truy cập rw, bạn cần 'PROT_WRITE | PROT_READ' cờ – Mali

+0

@Mali đúng, điều đó sẽ có ý nghĩa như một câu hỏi nếu anh ta mong đợi một sự cố khi đọc trong đối số cho printf đầu tiên, không phải khi ghi đè giá trị bằng '* a = 24'. Dù sao, tôi đã cố gắng để trang trải tất cả điều này trong câu trả lời của tôi. – Art

Trả lời

7

Có hai điều mà bạn đang quan sát:

  1. mprotect không được thiết kế để được sử dụng với các trang heap. Linux và OS X có cách xử lý hơi khác một chút (nhớ rằng OS X sử dụng Mach VM). OS X không thích các trang heap của nó bị giả mạo.

    Bạn có thể nhận hành vi giống hệt nhau trên cả hai hệ điều hành nếu bạn phân bổ trang của bạn qua mmap

    a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); 
    if (a == MAP_FAILED) 
        perror("mmap"); 
    
  2. Đây là một hạn chế của MMU của bạn (x86 trong trường hợp của tôi). MMU trong x86 không hỗ trợ các trang có thể ghi, nhưng không đọc được. Do đó, hãy đặt

    mprotect(a, pagesize, PROT_WRITE) 
    

    không làm gì cả. trong khi

    mprotect(a, pagesize, PROT_READ) 
    

    xóa ghi riêng và bạn nhận được SIGSEGV như mong đợi.

Ngoài ra mặc dù nó không có vẻ là một vấn đề ở đây, bạn nên hoặc biên dịch mã của bạn với -O0 hoặc thiết lập a-volatile int * để tránh bất kỳ optimisations trình biên dịch.

+0

1. bộ nhớ của 'mmap' đến từ đâu nếu không phải từ đống? 2. Tôi đã tìm ra điều này, nhưng tôi không tìm thấy bất kỳ nguồn nào cũng không gợi ý về điều đó. Đó là những gì tôi muốn giải quyết! Cảm ơn mọi người. – Aif

+2

Bộ nhớ trên 'mmap' xuất phát từ một khu vực miễn phí trong máy ảo của bạn. Nó khác biệt theo nghĩa là heap được quản lý bởi một thư viện 'malloc' không gian người dùng (mà lần lượt gọi' mmap') và cho phép phân bổ theo khối byte và VM được nhân quản lý và chỉ cho phép phân bổ theo khối của các trang. –

+0

2. Tôi vừa trải qua thiết kế MMU trên x86: Các bit truy cập không được thiết kế như 'rwx', nhưng là" không truy cập "," viết được bảo vệ "và" thực thi ". Không có cách nào để thiết lập một trang "viết, nhưng không đọc" trên phần cứng. –

1

Hầu hết các hệ điều hành và/hoặc kiến ​​trúc cpu tự động tạo ra nội dung có thể đọc được khi nó có thể ghi, do đó, PROT_WRITE thường ngụ ý nhất là PROT_READ. Nó chỉ đơn giản là không thể làm cho một cái gì đó có thể ghi mà không làm cho nó có thể đọc được.Những lý do có thể được suy đoán, hoặc không đáng để tạo ra một bit có thể đọc được trong MMU và cache, hoặc như trên một số kiến ​​trúc trước đó, bạn thực sự cần đọc MMU vào cache trước khi bạn có thể viết, do đó, làm cho một cái gì đó không thể đọc được tự động làm cho nó khó chịu.

Ngoài ra, có khả năng là printf cố gắng phân bổ từ bộ nhớ mà bạn đã bị hỏng với mprotect. Bạn muốn phân bổ một trang đầy đủ từ libc khi bạn đang thay đổi bảo vệ của nó, nếu không bạn sẽ thay đổi việc bảo vệ trang mà bạn không sở hữu đầy đủ và libc không mong đợi nó được bảo vệ. Trong thử nghiệm MacOS của bạn với PROT_READ đây là những gì sẽ xảy ra. printf phân bổ một số cấu trúc bên trong, cố truy cập chúng và treo khi chúng chỉ đọc.

+0

'printf/stdout' là dòng đệm, do đó, ông là tốt miễn là anh ta đặt một ngắt dòng ở cuối mỗi đầu ra. Vẫn in đến 'stderr' có thể là một ý tưởng tốt hơn. –

+0

Đó là những gì tôi mặc dù quá. Nhưng MacOS dường như không đồng ý. 'printf' trên MacOS không tuôn ra khi bị rơi sau cuộc gọi đầu tiên. – Art

+0

OS X 10.7 của tôi thực hiện khi tôi phân bổ trang thông qua 'mmap'. Ngay cả khi tôi thay đổi nó thành 'stderr' nó vẫn còn treo trước bản in đầu tiên trên' posix_memalign/mprotect (PROT_READ) '. –