| mprotect(2) | System Calls Manual | mprotect(2) |
mprotect, pkey_mprotect - контролирует доступ к области памяти
Стандартная библиотека языка C (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 is a combination of the following access flags: PROT_NONE or a bitwise OR of the other values in the following list:
Также (начиная с Linux 2.6.0), prot может содержать один из следующих установленных флагов:
Подобно 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.
POSIX says that the behavior of mprotect() is unspecified if it is applied to a region of memory that was not obtained via mmap(2).
В 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) {
/* Note: calling printf() from a signal handler is not safe
(and should not be done in production programs), since
printf() is not async-signal-safe; see signal-safety(7).
Nevertheless, we use printf() here as a simple way of
showing that the handler was called. */
printf("Got SIGSEGV at address: %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");
/* Allocate a buffer aligned on a page boundary;
initial protection is PROT_READ | PROT_WRITE. */
buffer = memalign(pagesize, 4 * pagesize);
if (buffer == NULL)
handle_error("memalign");
printf("Start of region: %p\n", buffer);
if (mprotect(buffer + pagesize * 2, pagesize,
PROT_READ) == -1)
handle_error("mprotect");
for (char *p = buffer ; ; )
*(p++) = 'a';
printf("Loop completed\n"); /* Should never happen */
exit(EXIT_SUCCESS); }
Русский перевод этой страницы руководства разработал(и) 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> и Kirill Rekhov <krekhov.dev@gmail.com>
Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.
Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу списка рассылки русских переводчиков.
| 15 июня 2024 г. | Справочные страницы Linux 6.9.1 |