Поведение PROT_READ и PROT_WRITE с помощью mprotect
Я пытался использовать mprotect
против чтения сначала, а потом писать.
Здесь мой код
#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;
}
Под Linux вот результаты:
Вот вывод для PROT_WRITE
:
$ ./main
a = 42
a = 24
и для PROT_READ
$ ./main
a = 42
Segmentation fault
Под Mac OS X 10.7:
Вот вывод для PROT_WRITE
:
$ ./main
a = 42
a = 24
и для PROT_READ
$ ./main
[1] 2878 bus error ./main
Пока я понимаю, что поведение OSX / Linux может быть другим, но я не понимаю, почему PROT_WRITE
не вылетает программа при чтении значения с printf
,
Может кто-нибудь объяснить эту часть?
2 ответа
Есть две вещи, которые вы наблюдаете:
mprotect
не был предназначен для использования с кучей страниц. В Linux и OS X обработка кучи несколько отличается (помните, что OS X использует Mach VM). OS X не любит подделывать кучи страниц.Вы можете получить одинаковое поведение в обеих ОС, если выделите свою страницу через
mmap
a = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (a == MAP_FAILED) perror("mmap");
Это ограничение вашего MMU (x86 в моем случае). MMU в x86 не поддерживает доступные для записи, но не читаемые страницы. Таким образом, установка
mprotect(a, pagesize, PROT_WRITE)
ничего не делает. в то время как
mprotect(a, pagesize, PROT_READ)
удалены записи привилегий и вы получите SIGSEGV, как и ожидалось.
Также, хотя здесь это не кажется проблемой, вы должны либо скомпилировать свой код с -O0
или установить a
в volatile int *
чтобы избежать каких-либо оптимизаций компилятора.
Большинство операционных систем и / или архитектур процессоров автоматически делают что-то читаемым, когда оно доступно для записи, поэтому PROT_WRITE
чаще всего подразумевает PROT_READ
также. Просто невозможно сделать что-то доступное для записи, не сделав его читабельным. Причины можно спекулировать: либо не стоит делать дополнительный бит читаемости в MMU и кешах, либо, как это было на некоторых более ранних архитектурах, вам действительно нужно прочитать MMU в кеш, прежде чем вы сможете писать, поэтому создание чего-то нечитаемого автоматически делает его неписываемым.
Кроме того, вероятно, что printf
пытается выделить из памяти, что вы повреждены с mprotect
, Вы хотите выделить полную страницу из libc при изменении ее защиты, в противном случае вы будете менять защиту страницы, которой вы не владеете полностью, и libc не ожидает, что она будет защищена. На вашем тесте MacOS с PROT_READ
это то, что происходит. printf
выделяет некоторые внутренние структуры, пытается получить к ним доступ и вылетает, когда они доступны только для чтения.