Blame man7/pkeys.7

Packit 7cfc04
.\" Copyright (C) 2016 Intel Corporation
Packit 7cfc04
.\"
Packit 7cfc04
.\" %%%LICENSE_START(VERBATIM)
Packit 7cfc04
.\" Permission is granted to make and distribute verbatim copies of this
Packit 7cfc04
.\" manual provided the copyright notice and this permission notice are
Packit 7cfc04
.\" preserved on all copies.
Packit 7cfc04
.\"
Packit 7cfc04
.\" Permission is granted to copy and distribute modified versions of this
Packit 7cfc04
.\" manual under the conditions for verbatim copying, provided that the
Packit 7cfc04
.\" entire resulting derived work is distributed under the terms of a
Packit 7cfc04
.\" permission notice identical to this one.
Packit 7cfc04
.\"
Packit 7cfc04
.\" Since the Linux kernel and libraries are constantly changing, this
Packit 7cfc04
.\" manual page may be incorrect or out-of-date.  The author(s) assume no
Packit 7cfc04
.\" responsibility for errors or omissions, or for damages resulting from
Packit 7cfc04
.\" the use of the information contained herein.  The author(s) may not
Packit 7cfc04
.\" have taken the same level of care in the production of this manual,
Packit 7cfc04
.\" which is licensed free of charge, as they might when working
Packit 7cfc04
.\" professionally.
Packit 7cfc04
.\"
Packit 7cfc04
.\" Formatted or processed versions of this manual, if unaccompanied by
Packit 7cfc04
.\" the source, must acknowledge the copyright and authors of this work.
Packit 7cfc04
.\" %%%LICENSE_END
Packit 7cfc04
.\"
Packit 7cfc04
.TH PKEYS 7 2017-09-15 "Linux" "Linux Programmer's Manual"
Packit 7cfc04
.SH NAME
Packit 7cfc04
pkeys \- overview of Memory Protection Keys
Packit 7cfc04
.SH DESCRIPTION
Packit 7cfc04
Memory Protection Keys (pkeys) are an extension to existing
Packit 7cfc04
page-based memory permissions.
Packit 7cfc04
Normal page permissions using
Packit 7cfc04
page tables require expensive system calls and TLB invalidations
Packit 7cfc04
when changing permissions.
Packit 7cfc04
Memory Protection Keys provide a mechanism for changing
Packit 7cfc04
protections without requiring modification of the page tables on
Packit 7cfc04
every permission change.
Packit 7cfc04
.PP
Packit 7cfc04
To use pkeys, software must first "tag" a page in the page tables
Packit 7cfc04
with a pkey.
Packit 7cfc04
After this tag is in place, an application only has
Packit 7cfc04
to change the contents of a register in order to remove write
Packit 7cfc04
access, or all access to a tagged page.
Packit 7cfc04
.PP
Packit 7cfc04
Protection keys work in conjunction with the existing
Packit 7cfc04
.BR PROT_READ /
Packit 7cfc04
.BR PROT_WRITE /
Packit 7cfc04
.BR PROT_EXEC
Packit 7cfc04
permissions passed to system calls such as
Packit 7cfc04
.BR mprotect (2)
Packit 7cfc04
and
Packit 7cfc04
.BR mmap (2),
Packit 7cfc04
but always act to further restrict these traditional permission
Packit 7cfc04
mechanisms.
Packit 7cfc04
.PP
Packit 7cfc04
If a process performs an access that violates pkey
Packit 7cfc04
restrictions, it receives a
Packit 7cfc04
.BR SIGSEGV
Packit 7cfc04
signal.
Packit 7cfc04
See
Packit 7cfc04
.BR sigaction (2)
Packit 7cfc04
for details of the information available with that signal.
Packit 7cfc04
.PP
Packit 7cfc04
To use the pkeys feature, the processor must support it, and the kernel
Packit 7cfc04
must contain support for the feature on a given processor.
Packit 7cfc04
As of early 2016 only future Intel x86 processors are supported,
Packit 7cfc04
and this hardware supports 16 protection keys in each process.
Packit 7cfc04
However, pkey 0 is used as the default key, so a maximum of 15
Packit 7cfc04
are available for actual application use.
Packit 7cfc04
The default key is assigned to any memory region for which a
Packit 7cfc04
pkey has not been explicitly assigned via
Packit 7cfc04
.BR pkey_mprotect (2).
Packit 7cfc04
.PP
Packit 7cfc04
Protection keys have the potential to add a layer of security and
Packit 7cfc04
reliability to applications.
Packit 7cfc04
But they have not been primarily designed as
Packit 7cfc04
a security feature.
Packit 7cfc04
For instance, WRPKRU is a completely unprivileged
Packit 7cfc04
instruction, so pkeys are useless in any case that an attacker controls
Packit 7cfc04
the PKRU register or can execute arbitrary instructions.
Packit 7cfc04
.PP
Packit 7cfc04
Applications should be very careful to ensure that they do not "leak"
Packit 7cfc04
protection keys.
Packit 7cfc04
For instance, before calling
Packit 7cfc04
.BR pkey_free (2),
Packit 7cfc04
the application should be sure that no memory has that pkey assigned.
Packit 7cfc04
If the application left the freed pkey assigned, a future user of
Packit 7cfc04
that pkey might inadvertently change the permissions of an unrelated
Packit 7cfc04
data structure, which could impact security or stability.
Packit 7cfc04
The kernel currently allows in-use pkeys to have
Packit 7cfc04
.BR pkey_free (2)
Packit 7cfc04
called on them because it would have processor or memory performance
Packit 7cfc04
implications to perform the additional checks needed to disallow it.
Packit 7cfc04
Implementation of the necessary checks is left up to applications.
Packit 7cfc04
Applications may implement these checks by searching the
Packit 7cfc04
.IR /proc/[pid]/smaps
Packit 7cfc04
file for memory regions with the pkey assigned.
Packit 7cfc04
Further details can be found in
Packit 7cfc04
.BR proc (5).
Packit 7cfc04
.PP
Packit 7cfc04
Any application wanting to use protection keys needs to be able
Packit 7cfc04
to function without them.
Packit 7cfc04
They might be unavailable because the hardware that the
Packit 7cfc04
application runs on does not support them, the kernel code does
Packit 7cfc04
not contain support, the kernel support has been disabled, or
Packit 7cfc04
because the keys have all been allocated, perhaps by a library
Packit 7cfc04
the application is using.
Packit 7cfc04
It is recommended that applications wanting to use protection
Packit 7cfc04
keys should simply call
Packit 7cfc04
.BR pkey_alloc (2)
Packit 7cfc04
and test whether the call succeeds,
Packit 7cfc04
instead of attempting to detect support for the
Packit 7cfc04
feature in any other way.
Packit 7cfc04
.PP
Packit 7cfc04
Although unnecessary, hardware support for protection keys may be
Packit 7cfc04
enumerated with the
Packit 7cfc04
.I cpuid
Packit 7cfc04
instruction.
Packit 7cfc04
Details of how to do this can be found in the Intel Software
Packit 7cfc04
Developers Manual.
Packit 7cfc04
The kernel performs this enumeration and exposes the information in
Packit 7cfc04
.IR /proc/cpuinfo
Packit 7cfc04
under the "flags" field.
Packit 7cfc04
The string "pku" in this field indicates hardware support for protection
Packit 7cfc04
keys and the string "ospke" indicates that the kernel contains and has
Packit 7cfc04
enabled protection keys support.
Packit 7cfc04
.PP
Packit 7cfc04
Applications using threads and protection keys should be especially
Packit 7cfc04
careful.
Packit 7cfc04
Threads inherit the protection key rights of the parent at the time
Packit 7cfc04
of the
Packit 7cfc04
.BR clone (2),
Packit 7cfc04
system call.
Packit 7cfc04
Applications should either ensure that their own permissions are
Packit 7cfc04
appropriate for child threads at the time when
Packit 7cfc04
.BR clone (2)
Packit 7cfc04
is called, or ensure that each child thread can perform its
Packit 7cfc04
own initialization of protection key rights.
Packit 7cfc04
.\"
Packit 7cfc04
.SS Signal Handler Behavior
Packit 7cfc04
Each time a signal handler is invoked (including nested signals), the
Packit 7cfc04
thread is temporarily given a new, default set of protection key rights
Packit 7cfc04
that override the rights from the interrupted context.
Packit 7cfc04
This means that applications must re-establish their desired protection
Packit 7cfc04
key rights upon entering a signal handler if the desired rights differ
Packit 7cfc04
from the defaults.
Packit 7cfc04
The rights of any interrupted context are restored when the signal
Packit 7cfc04
handler returns.
Packit 7cfc04
.PP
Packit 7cfc04
This signal behavior is unusual and is due to the fact that the x86 PKRU
Packit 7cfc04
register (which stores protection key access rights) is managed with the
Packit 7cfc04
same hardware mechanism (XSAVE) that manages floating-point registers.
Packit 7cfc04
The signal behavior is the same as that of floating-point registers.
Packit 7cfc04
.\"
Packit 7cfc04
.SS Protection Keys system calls
Packit 7cfc04
The Linux kernel implements the following pkey-related system calls:
Packit 7cfc04
.BR pkey_mprotect (2),
Packit 7cfc04
.BR pkey_alloc (2),
Packit 7cfc04
and
Packit 7cfc04
.BR pkey_free (2).
Packit 7cfc04
.PP
Packit 7cfc04
The Linux pkey system calls are available only if the kernel was
Packit 7cfc04
configured and built with the
Packit 7cfc04
.BR CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
Packit 7cfc04
option.
Packit 7cfc04
.SH EXAMPLE
Packit 7cfc04
.PP
Packit 7cfc04
The program below allocates a page of memory with read and write permissions.
Packit 7cfc04
It then writes some data to the memory and successfully reads it
Packit 7cfc04
back.
Packit 7cfc04
After that, it attempts to allocate a protection key and
Packit 7cfc04
disallows access to the page by using the WRPKRU instruction.
Packit 7cfc04
It then tries to access the page,
Packit 7cfc04
which we now expect to cause a fatal signal to the application.
Packit 7cfc04
.PP
Packit 7cfc04
.in +4n
Packit 7cfc04
.EX
Packit 7cfc04
.RB "$" " ./a.out"
Packit 7cfc04
buffer contains: 73
Packit 7cfc04
about to read buffer again...
Packit 7cfc04
Segmentation fault (core dumped)
Packit 7cfc04
.EE
Packit 7cfc04
.in
Packit 7cfc04
.SS Program source
Packit 7cfc04
\&
Packit 7cfc04
.EX
Packit 7cfc04
#define _GNU_SOURCE
Packit 7cfc04
#include <unistd.h>
Packit 7cfc04
#include <sys/syscall.h>
Packit 7cfc04
#include <stdio.h>
Packit 7cfc04
#include <sys/mman.h>
Packit 7cfc04
Packit 7cfc04
static inline void
Packit 7cfc04
wrpkru(unsigned int pkru)
Packit 7cfc04
{
Packit 7cfc04
    unsigned int eax = pkru;
Packit 7cfc04
    unsigned int ecx = 0;
Packit 7cfc04
    unsigned int edx = 0;
Packit 7cfc04
Packit 7cfc04
    asm volatile(".byte 0x0f,0x01,0xef\\n\\t"
Packit 7cfc04
                 : : "a" (eax), "c" (ecx), "d" (edx));
Packit 7cfc04
}
Packit 7cfc04
Packit 7cfc04
int
Packit 7cfc04
pkey_set(int pkey, unsigned long rights, unsigned long flags)
Packit 7cfc04
{
Packit 7cfc04
    unsigned int pkru = (rights << (2 * pkey));
Packit 7cfc04
    return wrpkru(pkru);
Packit 7cfc04
}
Packit 7cfc04
Packit 7cfc04
int
Packit 7cfc04
pkey_mprotect(void *ptr, size_t size, unsigned long orig_prot,
Packit 7cfc04
              unsigned long pkey)
Packit 7cfc04
{
Packit 7cfc04
    return syscall(SYS_pkey_mprotect, ptr, size, orig_prot, pkey);
Packit 7cfc04
}
Packit 7cfc04
Packit 7cfc04
int
Packit 7cfc04
pkey_alloc(void)
Packit 7cfc04
{
Packit 7cfc04
    return syscall(SYS_pkey_alloc, 0, 0);
Packit 7cfc04
}
Packit 7cfc04
Packit 7cfc04
int
Packit 7cfc04
pkey_free(unsigned long pkey)
Packit 7cfc04
{
Packit 7cfc04
    return syscall(SYS_pkey_free, pkey);
Packit 7cfc04
}
Packit 7cfc04
Packit 7cfc04
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \\
Packit 7cfc04
                           } while (0)
Packit 7cfc04
Packit 7cfc04
int
Packit 7cfc04
main(void)
Packit 7cfc04
{
Packit 7cfc04
    int status;
Packit 7cfc04
    int pkey;
Packit 7cfc04
    int *buffer;
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     *Allocate one page of memory
Packit 7cfc04
     */
Packit 7cfc04
    buffer = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
Packit 7cfc04
                  MAP_ANONYMOUS | MAP_PRIVATE, \-1, 0);
Packit 7cfc04
    if (buffer == MAP_FAILED)
Packit 7cfc04
        errExit("mmap");
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     * Put some random data into the page (still OK to touch)
Packit 7cfc04
     */
Packit 7cfc04
    *buffer = __LINE__;
Packit 7cfc04
    printf("buffer contains: %d\\n", *buffer);
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     * Allocate a protection key:
Packit 7cfc04
     */
Packit 7cfc04
    pkey = pkey_alloc();
Packit 7cfc04
    if (pkey == \-1)
Packit 7cfc04
        errExit("pkey_alloc");
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     * Disable access to any memory with "pkey" set,
Packit 7cfc04
     * even though there is none right now
Packit 7cfc04
     */
Packit 7cfc04
    status = pkey_set(pkey, PKEY_DISABLE_ACCESS, 0);
Packit 7cfc04
    if (status)
Packit 7cfc04
        errExit("pkey_set");
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     * Set the protection key on "buffer".
Packit 7cfc04
     * Note that it is still read/write as far as mprotect() is
Packit 7cfc04
     * concerned and the previous pkey_set() overrides it.
Packit 7cfc04
     */
Packit 7cfc04
    status = pkey_mprotect(buffer, getpagesize(),
Packit 7cfc04
                           PROT_READ | PROT_WRITE, pkey);
Packit 7cfc04
    if (status == -1)
Packit 7cfc04
        errExit("pkey_mprotect");
Packit 7cfc04
Packit 7cfc04
    printf("about to read buffer again...\\n");
Packit 7cfc04
Packit 7cfc04
    /*
Packit 7cfc04
     * This will crash, because we have disallowed access
Packit 7cfc04
     */
Packit 7cfc04
    printf("buffer contains: %d\\n", *buffer);
Packit 7cfc04
Packit 7cfc04
    status = pkey_free(pkey);
Packit 7cfc04
    if (status == -1)
Packit 7cfc04
        errExit("pkey_free");
Packit 7cfc04
Packit 7cfc04
    exit(EXIT_SUCCESS);
Packit 7cfc04
}
Packit 7cfc04
.EE
Packit 7cfc04
.SH SEE ALSO
Packit 7cfc04
.BR pkey_alloc (2),
Packit 7cfc04
.BR pkey_free (2),
Packit 7cfc04
.BR pkey_mprotect (2),
Packit 7cfc04
.BR sigaction (2)
Packit 7cfc04
.SH COLOPHON
Packit 7cfc04
This page is part of release 4.15 of the Linux
Packit 7cfc04
.I man-pages
Packit 7cfc04
project.
Packit 7cfc04
A description of the project,
Packit 7cfc04
information about reporting bugs,
Packit 7cfc04
and the latest version of this page,
Packit 7cfc04
can be found at
Packit 7cfc04
\%https://www.kernel.org/doc/man\-pages/.