table of contents
MPROTECT(2) | Руководство программиста Linux | MPROTECT(2) |
ИМЯ¶
mprotect - контролирует доступ к области памяти
ОБЗОР¶
#include <sys/mman.h> int mprotect(const void *addr, size_t len, int prot);
ОПИСАНИЕ¶
Вызов mprotect() изменяет параметры защиты страниц памяти вызывающего процесса, которые содержатся, даже частично, в адресном диапазоне [addr, addr+len-1]. Значение addr должно быть выровнено на границу страницы.
Если вызывающий процесс производит запрещённый доступ к памяти, то ядро посылает процессу сигнал SIGSEGV.
Значением prot может быть или PROT_NONE, или побитово сложенные значения из следующего списка:
- PROT_NONE
- Доступ к памяти запрещён.
- PROT_READ
- Память можно читать.
- PROT_WRITE
- Память можно изменять.
- PROT_EXEC
- Память можно выполнять.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ¶
При нормальном завершении работы mprotect() возвращает ноль. При ошибках возвращается -1, а переменной errno присваивается соответствующее значение.
ОШИБКИ¶
- EACCES
- Нельзя задать этот вид доступа. Например, это может случиться, если при вызове mmap(2) файл доступен только на чтение, а запрос mprotect() был PROT_WRITE.
- EINVAL
- Значение addr не является правильным указателем или не кратен размеру системной страницы.
- ENOMEM
- Не удалось выделить место под внутренние структуры ядра.
- ENOMEM
- Адреса в диапазоне [addr, addr+len-1] некорректны для адресного пространства процесса, или одна или более указанных страниц не отображена (до ядра версии 2.4.19 в этих случаях некорректно возвращалась ошибка EFAULT).
СООТВЕТСТВИЕ СТАНДАРТАМ¶
SVr4, POSIX.1-2001. В POSIX указано, что поведение mprotect() не определено, если задаётся область памяти, которая не получена через mmap(2).
ЗАМЕЧАНИЯ¶
В Linux всегда можно вызвать mprotect() с любым адресом из адресного пространства процесса (за исключением области ядра vsyscall). В частности, это можно использовать для изменения отображений существующего кода на записываемые.
Отличия в работе между PROT_EXEC и PROT_READ зависят от архитектуры и версии ядра. На некоторых аппаратных архитектурах (например, i386) PROT_WRITE подразумевает PROT_READ.
В POSIX.1-2001 сказано, что реализация может разрешить доступ отличный от указанного в prot, но для доступа на запись должен быть обязательно установлен флаг PROT_WRITE, и любой доступ должен быть запрещён, если установлен флаг PROT_NONE.
ПРИМЕР¶
Программа, представленная далее, выделяет четыре страницы памяти, делает третью доступной только на чтение, а затем запускает цикл, который проходит по выделенной области, меняя байты.
Результат работы программы:
$ ./a.out Начало области: 0x804c000 Получен SIGSEGV при адресе: 0x804e000
Исходный код программы¶
#include <unistd.h> #include <signal.h> #include <stdio.h> #include <malloc.h> #include <stdlib.h> #include <errno.h> #include <sys/mman.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) char *buffer; static void handler(int sig, siginfo_t *si, void *unused) {
printf("Получен SIGSEGV при адресе: 0x%lx\n",
(long) si->si_addr);
exit(EXIT_FAILURE); } int main(int argc, char *argv[]) {
char *p;
int pagesize;
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = handler;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
handle_error("sigaction");
pagesize = sysconf(_SC_PAGE_SIZE);
if (pagesize == -1)
handle_error("sysconf");
/* выделить буфер с выравниванием на границу страницы;
начальная защита: PROT_READ | PROT_WRITE */
buffer = memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Начало области: 0x%lx\n", (long) buffer);
if (mprotect(buffer + pagesize * 2, pagesize,
PROT_READ) == -1)
handle_error("mprotect");
for (p = buffer ; ; )
*(p++) = 'a';
printf("Цикл завершён\n"); /* никогда не должно случиться */
exit(EXIT_SUCCESS); }
СМОТРИТЕ ТАКЖЕ¶
2011-09-08 | Linux |