Главная » 2017 » Ноябрь » 21 » man 2 pkey_mprotect
01:18
man 2 pkey_mprotect

SEO sprint - Всё для максимальной раскрутки!





ИМЯ


mprotect, pkey_mprotect - контролирует доступ к области памяти



ОБЗОР


#include <sys/mman.h>

int mprotect(void *addr, size_t len, int prot);
int pkey_mprotect(void *addr, size_t len, int prot, int pkey);



ОПИСАНИЕ


Вызов mprotect() изменяет параметры доступа страниц памяти вызывающего процесса,
которые содержатся, даже частично, в адресном диапазоне [addr, addr+len-1].
Значение addr должно быть выровнено на границу страницы.

Если вызывающий процесс нарушает защиту доступа к памяти, то ядро посылает
процессу сигнал SIGSEGV.

Значение prot представляет собой комбинацию следующих флагов доступа: PROT_NONE
или побитово сложенные другие значения из следующего списка:

PROT_NONE Доступ к памяти запрещён.

PROT_READ Память можно читать.

PROT_WRITE Память можно изменять.

PROT_EXEC Память можно выполнять.

PROT_SEM (начиная с Linux 2.5.7)
Память можно использовать для атомарных операций. Этот флаг появился
как часть реализации futex(2) (для гарантии способности выполнять
атомарные операции, требуемые таким командам как FUTEX_WAIT), но пока
не используется ни в одной архитектуре.

PROT_SAO (начиная с Linux 2.6.26)
Память должна иметь строгий порядок доступа. Это свойство есть только в
архитектуре PowerPC (в спецификации архитектуры версии 2.06 добавлено
свойство ЦП SAO и оно доступно, например, на POWER 7 или PowerPC A2).

Также (начиная с Linux 2.6.0), prot может содержать один из следующих
установленных флагов:

PROT_GROWSUP
Применить режим защиты до конца отображения, которое растёт вверх
(такие отображения создаются для области стека например в архитектуре
HP-PARISC, где стек растёт вверх).

PROT_GROWSDOWN
Применить режим защиты до начала отображения, которое растёт вниз
(которое должно быть сегментом стека или сегментом, отображённым с
установленным флагом MAP_GROWSDOWN).

Подобно mprotect(), вызов pkey_mprotect() изменяет защиту страниц, указанных addr
и len. Аргумент pkey содержит ключ защиты (смотрите pkeys(7)), назначаемый памяти.
Ключ защиты должен быть выделен с помощью pkey_alloc(2) до передачи в
pkey_mprotect(). Пример использования этого системного вызова смотрите в pkeys(7).

EINVAL Значение addr не является правильным указателем или не кратен размеру
системной страницы.

EINVAL (pkey_mprotect()) pkey не был выделен с помощью pkey_alloc(2).

EINVAL В prot указаны оба флага, PROT_GROWSUP и PROT_GROWSDOWN.

EINVAL Указано неверное значение в prot.

EINVAL (архитектура PowerPC ) В prot указан PROT_SAO, но недоступно аппаратное
свойство SAO.

ENOMEM Не удалось выделить место под внутренние структуры ядра.

ENOMEM Адреса в диапазоне [addr, addr+len-1] некорректны для адресного
пространства процесса, или одна или более указанных страниц не отображена
(до ядра версии 2.4.19 в этих случаях некорректно возвращалась ошибка
EFAULT).

ENOMEM Изменение защиты области памяти привело бы к превышению разрешённого
максимума на количество отображений с различающимися атрибутами (защита на
чтение и на чтение/запись). Например, защита диапазона PROT_READ в середине
области, которая сейчас защищена PROT_READ|PROT_WRITE, привела бы к трём
отображениям: два отображения на концах, доступных на чтение/запись и
доступное только для чтение отображение посередине.



ВЕРСИИ


Системный вызов pkey_mprotect() появился в Linux 4.9. Поддержка в glibc пока
отсутствует.



СООТВЕТСТВИЕ СТАНДАРТАМ


mprotect(): В POSIX.1-2001, POSIX.1-2008, SVr4 сказано, что поведение mprotect()
не определено, если переданная область памяти не получена через mmap(2).

Вызов pkey_mprotect является непереносимым расширением Linux.



ЗАМЕЧАНИЯ


В Linux всегда можно вызвать mprotect() с любым адресом из адресного пространства
процесса (за исключением области ядра vsyscall). В частности, это можно
использовать для изменения отображений существующего кода на записываемые.

Отличается ли действие PROT_EXEC от PROT_READ зависит от архитектуры процессора,
версии ядра и состояния процесса. Если в флагах специализаций процессора
установлен READ_IMPLIES_EXEC (смотрите personality(2)), то указание PROT_READ
подразумевает добавление PROT_EXEC.

На некоторых аппаратных архитектурах (например, i386) PROT_WRITE подразумевает
PROT_READ.

В POSIX.1 сказано, что реализация может разрешить доступ отличный от указанного в
prot, но для доступа на запись должен быть обязательно установлен флаг PROT_WRITE,
и любой доступ должен быть запрещён, если установлен флаг PROT_NONE.

В приложениях нужно осторожно использовать mprotect() и pkey_mprotect() вместе. На
x86, если mprotect() используется с установленным в prot значением PROT_EXEC, то
pkey может быть выделен и установлен ядром в память неявным образом, но только
если до этого pkey был равен 0.

Результат работы программы:

$ ./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)

static char *buffer;

static void
handler(int sig, siginfo_t *si, void *unused)
{
/* Замечание: вызов printf() из обработчика сигнала небезопасен
(и не должен выполняться в готовых программах), так как
printf() не async-signal-safe; смотрите signal-safety(7).
Тем не менее, здесь мы используем printf(), так как это простой
способ показать когда вызывается обработчик. */

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);
for (p = buffer ; ; )
*(p++) = 'a';

printf("Цикл завершён\n"); /* никогда не должно случиться */
exit(EXIT_SUCCESS);
}



СМОТРИТЕ ТАКЖЕ


mmap(2), sysconf(3), pkeys(7)



Категория: (2) Системные вызовы ядра (функции языка Си) | Просмотров: 426 | Добавил: Администратор | Рейтинг: 0.0/0
Всего комментариев: 0
avatar