Provided by: manpages-ru-dev_4.21.0-2_all bug

ИМЯ

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

LIBRARY

       Standard C library (libc, -lc)

СИНТАКСИС

       #include <sys/mman.h>

       int mprotect(void addr[.len], size_t len, int prot);

       #define _GNU_SOURCE             /* смотрите feature_test_macros(7) */
       #include <sys/mman.h>

       int pkey_mprotect(void addr[.len], 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
              Apply  the  protection  mode  up to the end of a mapping that grows upwards.  (Such
              mappings are created for the stack area on  architectures—for  example,  HP-PARISC—
              that have an upwardly growing stack.)

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

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

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

       On success, mprotect()  and pkey_mprotect()  return zero.  On error,  these  system  calls
       return -1, and errno is set to indicate the error.

ОШИБКИ

       EACCES Нельзя  задать  этот  вид  доступа.  Например, это может случиться, если при вызове
              mmap(2) файл доступен только на чтение, а запрос mprotect() был PROT_WRITE.

       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 Addresses in the range [addr, addr+len-1] are invalid for the address space of  the
              process,  or  specify one or more pages that are not mapped.  (Before Linux 2.4.19,
              the error EFAULT was incorrectly produced for these cases.)

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

ВЕРСИИ

       Вызов pkey_mprotect() впервые появился в Linux 4.9; поддержка в библиотеке glibc добавлена
       в версии 2.27.

СТАНДАРТЫ

       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.

       В   системах  без  аппаратной  поддержки  ключей  защиты  pkey_mprotect()  всё  ещё  можно
       использовать,  но  значение  pkey  должно  быть  равно  -1.  При  таком  вызове   операция
       pkey_mprotect() эквивалентна mprotect().

ПРИМЕРЫ

       Программа,  представленная далее, показывает использование mprotect(). Она выделяет четыре
       страницы памяти, делает третью доступной только на чтение, а затем запускает цикл, который
       проходит по выделенной области, меняя байты.

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

           $ ./a.out
           Начало области:             0x804c000
           Получен SIGSEGV при адресе: 0x804e000

   Исходный код программы

       #include <malloc.h>
       #include <signal.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/mman.h>
       #include <unistd.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 при адресе: %p\n", si->si_addr);
           exit(EXIT_FAILURE);
       }

       int
       main(void)
       {
           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("Начало области:        %p\n", buffer);

           if (mprotect(buffer + pagesize * 2, pagesize,
                        PROT_READ) == -1)
               handle_error("mprotect");

           for (char *p = buffer ; ; )
               *(p++) = 'a';

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

СМ. ТАКЖЕ

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

ПЕРЕВОД

       Русский  перевод  этой страницы руководства был сделан aereiae <aereiae@gmail.com>, Alexey
       <a.chepugov@gmail.com>, Azamat Hackimov <azamat.hackimov@gmail.com>,  Dmitriy  S.  Seregin
       <dseregin@59.ru>,       Dmitry      Bolkhovskikh      <d20052005@yandex.ru>,      ITriskTI
       <ITriskTI@gmail.com>, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru>,  Иван
       Павлов <pavia00@gmail.com> и Малянов Евгений Викторович <maljanow@outlook.com>

       Этот  перевод  является  бесплатной  документацией;  прочитайте  Стандартную  общественную
       лицензию GNU версии 3 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

       Если  вы  обнаружите  ошибки  в  переводе этой страницы руководства, пожалуйста, отправьте
       электронное письмо на ⟨man-pages-ru-talks@lists.sourceforge.net⟩.