mprotect

Autres langues

Langue: ja

Version: 2007-06-02 (fedora - 25/11/07)

Section: 2 (Appels système)

名前

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 以外の値をビット毎の論理和 (bitwize-or) で指定する:

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

返り値

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

エラー

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

準拠

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

注意

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

PROT_EXECPROT_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 die(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
 
 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)
         die("sigaction");
 
     pagesize = sysconf(_SC_PAGE_SIZE);
     if (pagesize == -1)
         die("sysconf");
 
     /* Allocate a buffer aligned on a page boundary;
        initial protection is PROT_READ | PROT_WRITE */
 
     buffer = memalign(pagesize, 4 * pagesize);
     if (buffer == NULL)
         die("memalign");
 
     printf("Start of region:        0x%lx\n", (long) buffer);
 
     if (mprotect(buffer + pagesize * 2, pagesize,
                 PROT_NONE) == -1)
         die("mprotect");
 
     for (p = buffer ; ; )
         *(p++) = 'a';
 
     printf("Loop completed\n");     /* Should never happen */
     exit(EXIT_SUCCESS);
 }
 

関連項目

mmap(2), sysconf(3)