mprotect(2) メモリ領域の保護を設定する

書式

#include <sys/mman.h>


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

説明

mprotect() は、区間 [addraddr+len-1] のアドレス範囲を含む 呼び出し元のプロセスのメモリページのアクセス保護を変更する。 addr はページ境界に一致していなければならない。

呼び出し元のプロセスがアクセス保護に違反するようなメモリアクセスを 行おうとすると、カーネルはシグナル SIGSEGV をそのプロセスに対して生成する。

prot には、 PROT_NONE か、以下のリストの PROT_NONE 以外の値をビット毎の論理和 (bitwize-or) で指定する:

PROT_NONE
そのメモリには全くアクセスできない。
PROT_READ
そのメモリを読み取ることができる。
PROT_WRITE
そのメモリを変更できる。
PROT_EXEC
そのメモリは実行可能である。

返り値

成功した場合、 mprotect() は 0 を返す。エラーの場合は -1 が返り、 errno が適切に設定される。

エラー

EACCES
指定されたアクセスをメモリに設定することができない。 これは、例えば ファイルを読み取り専用で mmap(2) しており、その領域に対して mprotect() を呼び出して PROT_WRITE に設定しようとした場合に発生する。
EINVAL
addr が有効なポインタでないか、 システムのページサイズの倍数でない。
ENOMEM
カーネル内部の構造体を割り当てることができなかった。
ENOMEM
[addr, addr+len-1] という範囲のアドレスがプロセスのアドレス空間として不正であるか、 その範囲のアドレスがマップされていない 1 つ以上のページを指している (カーネル 2.4.19 より前では、この状況でエラー EFAULT が間違って生成されていた)。

準拠

SVr4, POSIX.1-2001. POSIX では、 mmap(2) 経由で獲得していないメモリ領域に対して mprotect() を行った場合の mprotect() の動作は未定義であるとされている。

注意

Linux では、(カーネル vsyscall 領域以外の) 任意のプロセスアドレス空間に対して mprotect() を呼び出すことが、常に許されている。 これは特に既存のコードマッピングを書き込み可能にするために使われる。

PROT_EXECPROT_READ と異なる影響を持つか否かは、アーキテクチャとカーネルのバージョンに依存する。 (i386 などの) いくつかのアーキテクチャでは、 PROT_WRITE をセットすると、暗黙のうちに PROT_READ がセットされる。

POSIX.1-2001 では、 prot で指定されていないアクセスを許可する実装を認めている。 ただし、最低限、 PROT_WRITE がセットされている場合にのみ書き込みアクセスが許可され、 PROT_NONE がセットされている場合にはアクセスは許可されない点だけは 満たす必要がある。

以下のプログラムは、メモリページを 4つ確保し、そのうち 3番目のページを 読み込み専用に設定する。その後で、確保した領域のアドレスの小さい方から 大きな方に向かって順番にバイト値を変更するループを実行する。

プログラムを実行した場合の一例を以下に示す。

$ ./a.out
Start of region:        0x804c000
Got SIGSEGV at address: 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("Got SIGSEGV at address: 0x%lx\n",
            (long) si->si_addr);
    exit(EXIT_FAILURE);
}
int
main(void)
{
    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");
    /* 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:        0x%lx\n", (long) buffer);
    if (mprotect(buffer + pagesize * 2, pagesize,
                PROT_READ) == -1)
        handle_error("mprotect");
    for (p = buffer ; ; )
        *(p++) = 'a';
    printf("Loop completed\n");     /* Should never happen */
    exit(EXIT_SUCCESS);
}

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.65 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。