mprotect — set protection on a region of memory
#include <sys/mman.h>
int
mprotect( |
const void * | addr, |
size_t * | len, | |
int | prot) ; |
mprotect
() changes
protection for the calling process's memory page(s)
containing any part of the address range in the interval
[addr
, addr
+len
−1]. addr
must be aligned to a page
boundary.
If the calling process tries to access memory in a manner
that violates the protection, then the kernel generates a
SIGSEGV
signal for the
process.
prot
is either
PROT_NONE
or a bitwise-or of
the other values in the following list:
PROT_NONE
The memory cannot be accessed at all.
PROT_READ
The memory can be read.
PROT_WRITE
The memory can be modified.
PROT_EXEC
The memory can be executed.
On success, mprotect
()
returns zero. On error, −1 is returned, and
errno
is set appropriately.
The memory cannot be given the specified access.
This can happen, for example, if you mmap(2) a file to
which you have read-only access, then ask mprotect
() to mark it PROT_WRITE
.
The memory cannot be accessed.
addr
is not
a valid pointer, or not a multiple of the system page
size.
Internal kernel structures could not be allocated.
Or: addresses in the range [addr
, addr
+len
] are invalid for the
address space of the process, or specify one or more
pages that are not mapped.
SVr4, POSIX.1-2001. 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).
On Linux it is always legal to call mprotect
() on any address in a process's
address space (except for the kernel vsyscall area). In
particular it can be used to change existing code mappings to
be writable.
Whether PROT_EXEC
has any
effect different from PROT_READ
is architecture- and kernel version-dependent. On some
hardware architectures (e.g., i386), PROT_WRITE
implies PROT_READ
.
POSIX.1-2001 says that an implementation may permit access
other than that specified in prot
, but at a minimum can only
allow write access if PROT_WRITE
has been set, and must not allow
any access if PROT_NONE
has
been set.
The program below allocates four pages of memory, makes the third of these pages read-only, and then executes a loop that walks upwards through the allocated region modifying bytes.
An example of what we might see when running the program is the following:
$ ./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) 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"); /* 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_NONE) == −1) handle_error("mprotect"); for (p = buffer ; ; ) *(p++) = 'a'; printf("Loop completed\n"); /* Should never happen */ exit(EXIT_SUCCESS); }