概要
straceの結果の常連mprotectをあまり理解してなかったのでメモ
mprotect(2)とは
mprotect(2)とはメモリ領域のアクセス許可を制御するシステムコール。
# include <sys/mman.h>
int mprotect(const void *addr, size_t len, int prot);
ポインタとサイズとビットの論理和を渡す。
フラグ
現在、以下の OR をとって組み合わせることが可能な保護ビット
フラグ名 | 概要 |
---|---|
PROT_NONE | 全く保護しません |
PROT_READ | ページは読取り可能です |
PROT_WRITE | ページは書込み可能です |
PROT_EXEC | ページは実行可能です |
エラー
成功した場合、 mprotect() は 0 を返す。エラーの場合は -1 が返り、 errno が適切に設定される。
errno | 概要 |
---|---|
EACCES | 指定されたアクセスをメモリーに設定することができない |
EINVAL | addr が有効なポインターでないか、 システムのページサイズの倍数でない |
ENOMEM | カーネル内部の構造体を割り当てることができなかった |
ENOMEM | [addr, addr+len-1] という範囲のアドレスがプロセスのアドレス空間として不正であるか、 その範囲のアドレスがマップされていない 1 つ以上のページを指している |
使用例
READ_ONLY領域のメモリへ書き込みを行うことでSIGSEGVを発生させるサンプルプログラム。
SIGSEGV自体はsigactionを使ってハンドリングした処理を実行。
mprotect.c
# 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(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");
// アライメントされたメモリを割り当てる
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);
}