pkeys

PKEYS(7)        Ð ÑководÑÑво пÑогÑаммиÑÑа Linux        PKEYS(7)



ÐÐЯ
       pkeys - Ð¾Ð±Ð·Ð¾Ñ ÐºÐ»ÑÑей заÑиÑÑ Ð¿Ð°Ð¼ÑÑи

ÐÐÐСÐÐÐÐ
       ÐлÑÑи заÑиÑÑ Ð¿Ð°Ð¼ÑÑи (pkeys) â ÑÑо ÑаÑÑиÑение
       ÑÑÑеÑÑвÑÑÑÐ¸Ñ Ð¿Ð¾ÑÑÑаниÑнÑÑ Ð¿Ñав на памÑÑÑ. ÐлÑ
       обÑÑнÑÑ Ð¿Ñав на ÑÑÑаниÑÑ Ð¸ÑполÑзÑÑÑÑÑ ÑÑÑаниÑнÑе
       ÑаблиÑÑ, ÑÑебÑÑÑие Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¸ пÑав
       заÑÑаÑнÑÑ ÑиÑÑемнÑÑ Ð²Ñзовов и аннÑлиÑованиÑ
       TLB. ÐлÑÑи заÑиÑÑ Ð¿Ð°Ð¼ÑÑи пÑедоÑÑавлÑÑÑ Ð¼ÐµÑанизм
       Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°ÑиÑÑ Ð±ÐµÐ· необÑодимоÑÑи
       изменÑÑÑ ÑÑÑаниÑнÑе ÑаблиÑÑ Ð¿Ñи каждом
       изменении пÑав.

       ЧÑÐ¾Ð±Ñ Ð¸ÑполÑзоваÑÑ pkeys, ÐÐ ÑнаÑала должно
       «помеÑиÑÑ» (tag) ÑÑÑаниÑÑ Ð² ÑÑÑаниÑнÑÑ ÑаблиÑаÑ
       знаÑением pkey. ÐоÑле ÑазмеÑÐµÐ½Ð¸Ñ ÑÑой меÑки
       Ð´Ð»Ñ ÑÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ð¿Ñав на запиÑÑ Ð¸Ð»Ð¸ веÑÑ Ð´Ð¾ÑÑÑп к
       помеÑенной ÑÑÑаниÑе пÑÐ¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð½Ñжно
       измениÑÑ ÑолÑко ÑодеÑжимое ÑегиÑÑÑа.

       ÐлÑÑи заÑиÑÑ Ð²Ð¼ÐµÑÑе Ñ ÑÑÑеÑÑвÑÑÑими пÑавами
       PROT_READ/ PROT_WRITE/ PROT_EXEC пеÑедаÑÑÑÑ Ð² ÑиÑÑемнÑе
       вÑзовÑ, Ñакие как mprotect(2) и mmap(2), но вÑегда
       ÑÑиÑаÑÑÑÑ ÐºÐ°Ðº дополниÑелÑное огÑаниÑение к
       ÑÑÑеÑÑвÑÑÑим ÑÑадиÑионнÑм меÑанизмам пÑав
       доÑÑÑпа.

       ÐÑли пÑоÑеÑÑ Ð¾ÑÑÑеÑÑвлÑÐµÑ Ð´Ð¾ÑÑÑп, наÑÑÑаÑÑий
       огÑаниÑÐµÐ½Ð¸Ñ pkey, Ñо он полÑÑÐ°ÐµÑ Ñигнал SIGSEGV.
       ÐодÑобнÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾Ð± ÑÑом Ñигнале ÑмоÑÑиÑе
       в sigaction(2).

       ЧÑÐ¾Ð±Ñ Ð¸ÑполÑзоваÑÑ ÑвойÑÑво pkeys, ÑÑо должен
       поддеÑживаÑÑ Ð¿ÑоÑеÑÑоÑ, а ÑдÑо должно
       вклÑÑаÑÑ Ð¿Ð¾Ð´Ð´ÐµÑÐ¶ÐºÑ ÑÑого ÑвойÑÑва Ð´Ð»Ñ ÑÑого
       пÑоÑеÑÑоÑа. РнаÑÐ°Ð»Ñ 2016 года ÑÑо оÑноÑиÑÑÑ
       ÑолÑко к бÑдÑÑим пÑоÑеÑÑоÑам Intel x86, и даннаÑ
       аппаÑаÑÑÑа поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ 16 клÑÑей заÑиÑÑ Ð½Ð°
       каждÑй пÑоÑеÑÑ. Ðднако pkey 0 иÑполÑзÑеÑÑÑ ÐºÐ°Ðº
       клÑÑ Ð¿Ð¾ ÑмолÑаниÑ, поÑÑÐ¾Ð¼Ñ Ð´Ð»Ñ Ð¿ÑиложениÑ
       доÑÑÑпно ÑолÑко 15. ÐлÑÑ Ð¿Ð¾ ÑмолÑаниÑ
       назнаÑаеÑÑÑ Ð»Ñбой облаÑÑи памÑÑи, длÑ
       коÑоÑой pkey не бÑл назнаÑен ÑвнÑм обÑазом Ñ
       помоÑÑÑ pkey_mprotect(2).

       ÐоÑенÑиалÑно, клÑÑи заÑиÑÑ Ð¼Ð¾Ð³ÑÑ Ð´Ð¾Ð±Ð°Ð²Ð¸ÑÑ
       ÑÑÐ¾Ð²ÐµÐ½Ñ Ð±ÐµÐ·Ð¾Ð¿Ð°ÑноÑÑи и надежноÑÑи
       пÑиложений. Ðо, пÑежде вÑего, они не
       ÑазÑабаÑÑвалиÑÑ ÐºÐ°Ðº ÑÑедÑÑво заÑиÑÑ. ÐапÑимеÑ,
       WRPKRU â ÑÑо полноÑÑÑÑ Ð½ÐµÐ¿ÑивилегиÑованнаÑ
       инÑÑÑÑкÑиÑ, поÑÑÐ¾Ð¼Ñ pkeys беÑполезнÑ, когда
       аÑакÑÑÑий конÑÑолиÑÑÐµÑ ÑегиÑÑÑ PKRU или можеÑ
       вÑполнÑÑÑ Ð»ÑбÑе инÑÑÑÑкÑии.

       ÐÑÐ¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ ÑледиÑÑ Ð·Ð° Ñем, ÑÑÐ¾Ð±Ñ Ð¸Ñ
       клÑÑи заÑиÑÑ Ð½Ðµ «не ÑÑекли». ÐапÑимеÑ, пеÑед
       вÑзовом pkey_free(2) пÑиложение должно
       пÑовеÑиÑÑ, ÑÑо pkey не назнаÑен памÑÑи. ÐÑли
       пÑиложение оÑÑÐ°Ð²Ð¸Ñ Ð½Ð°Ð·Ð½Ð°ÑеннÑм
       оÑвобождÑннÑй pkey,  Ñо бÑдÑÑий полÑзоваÑелÑ
       ÑÑого pkey Ð¼Ð¾Ð¶ÐµÑ Ð½ÐµÐ¿ÑеднамеÑенно измениÑÑ
       пÑава на не оÑноÑÑÑÑÑÑÑ Ðº Ð´ÐµÐ»Ñ ÑÑÑÑкÑÑÑÑ Ð´Ð°Ð½Ð½ÑÑ,
       ÑÑо Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑивеÑÑи к пÑоблемам Ñ
       безопаÑноÑÑÑÑ Ð¸Ð»Ð¸ ÑÑабилÑноÑÑÑÑ. РнаÑÑоÑÑее
       вÑÐµÐ¼Ñ ÑдÑо позволÑÐµÑ Ð²ÑзÑваÑÑ pkey_free(2) длÑ
       задейÑÑвованнÑÑ pkeys, Ñак как вÑполнение
       дополниÑелÑнÑÑ Ð¿ÑовеÑок повлиÑло Ð±Ñ Ð½Ð°
       пÑоизводиÑелÑноÑÑÑ Ð¿ÑоÑеÑÑоÑа или памÑÑи.
       РеализаÑÐ¸Ñ Ð½ÐµÐ¾Ð±ÑодимÑÑ Ð¿ÑовеÑок
       пеÑеложена на пÑиложение. ÐÑиложениÑ
       могÑÑ Ð½Ð°Ð¹Ñи облаÑÑи памÑÑи, коÑоÑÑм назнаÑен
       pkey, в Ñайле /proc/[pid]/smaps. ÐополниÑелÑнаÑ
       инÑоÑмаÑÐ¸Ñ Ð¿ÑедÑÑавлена в proc(5).

       ÐÑиложение, коÑоÑое ÑоÑÐµÑ Ð²Ð¾ÑполÑзоваÑÑÑÑ
       клÑÑами заÑиÑÑ, Ð¼Ð¾Ð¶ÐµÑ ÑабоÑаÑÑ Ð¸ без ниÑ. ÐлÑÑи
       могÑÑ Ð±ÑÑÑ Ð½ÐµÐ´Ð¾ÑÑÑÐ¿Ð½Ñ Ð¸Ð·-за оÑÑÑÑÑÑвиÑ
       аппаÑаÑной поддеÑжки ÑиÑÑеме, где
       запÑÑкаеÑÑÑ Ð¿Ñиложение, в коде ÑдÑа можеÑ
       не бÑÑÑ Ð¿Ð¾Ð´Ð´ÐµÑжки, поддеÑжка в ÑдÑе можеÑ
       бÑÑÑ Ð²ÑклÑÑена или вÑе клÑÑи Ñже
       задейÑÑÐ²Ð¾Ð²Ð°Ð½Ñ Ð±Ð¸Ð±Ð»Ð¸Ð¾Ñекой, коÑоÑÑÑ
       иÑполÑзÑÐµÑ Ð¿Ñиложение. РпÑиложениÑÑ
       ÑекомендÑеÑÑÑ Ð¿ÑоÑÑо вÑзÑваÑÑ pkey_alloc(2) и
       пÑовеÑÑÑÑ ÑÑпеÑноÑÑÑ Ð²ÑполнениÑ, а не пÑÑаÑÑÑÑ
       пÑовеÑÑÑÑ Ð½Ð°Ð»Ð¸Ñие поддеÑжки каким-Ñо дÑÑгим
       обÑазом.

       ХоÑÑ Ð¸ необÑзаÑелÑно, поддеÑÐ¶ÐºÑ ÐºÐ»ÑÑей
       заÑиÑÑ Ð² аппаÑаÑÑÑе можно опÑеделиÑÑ Ð¿Ð¾Ð¼Ð¾ÑÑÑ
       инÑÑÑÑкÑии cpuid. ÐÑ Ñом, как ÑÑо ÑделаÑÑ, ÑмоÑÑиÑе
       в пÑогÑаммном ÑÑководÑÑве ÑазÑабоÑÑика Intel.
       ЯдÑо опÑеделÑÐµÑ Ð½Ð°Ð»Ð¸Ñие поддеÑжки и
       вÑÐ²Ð¾Ð´Ð¸Ñ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð² /proc/cpuinfo в поле «flags».
       СÑÑока «pku» в ÑÑом поле ознаÑаеÑ, ÑÑо
       аппаÑаÑÑÑа поддеÑÐ¶Ð¸Ð²Ð°ÐµÑ ÐºÐ»ÑÑи заÑиÑÑ, а
       ÑÑÑока «ospke» ознаÑаеÑ, ÑÑо ÑдÑо ÑодеÑжиÑ
       вклÑÑÑннÑÑ Ð¿Ð¾Ð´Ð´ÐµÑÐ¶ÐºÑ Ð·Ð°ÑиÑÑ.

       ÐÑли пÑиложение иÑполÑзÑÐµÑ Ð½Ð¸Ñи и клÑÑи
       заÑиÑÑ, Ñо нÑжно бÑÑÑ Ð¾Ñобенно оÑÑоÑожнÑм. ÐиÑи
       наÑледÑÑÑ Ð¿Ñава клÑÑей заÑиÑÑ ÑодиÑÐµÐ»Ñ Ð¿Ñи
       вÑполнении ÑиÑÑемного вÑзова clone(2).
       ÐÑÐ¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ ÑбедиÑÑÑÑ, ÑÑо иÑ
       ÑобÑÑвеннÑе пÑава подÑодÑÑ Ð´Ð»Ñ Ð´Ð¾ÑеÑниÑ
       ниÑей до вÑзова clone(2) или вÑполнÑÑÑ
       иниÑиализаÑÐ¸Ñ Ð¿Ñав клÑÑей заÑиÑÑ Ð² ÑамиÑ
       ниÑÑÑ.

   Ðоведение обÑабоÑÑика Ñигналов
       ÐаждÑй Ñаз, когда вÑзÑваеÑÑÑ Ð¾Ð±ÑабоÑÑик
       Ñигнала (вклÑÑÐ°Ñ Ð²Ð»Ð¾Ð¶ÐµÐ½Ð½Ñе ÑигналÑ), ниÑи
       вÑеменно даÑÑÑÑ Ð½Ð¾Ð²Ñй Ð½Ð°Ð±Ð¾Ñ Ð¿Ñав клÑÑа заÑиÑÑ
       по ÑмолÑаниÑ, коÑоÑÑй заменÑÐµÑ Ð¿Ñава
       пÑеÑванного конÑекÑÑа. ÐÑо ознаÑаеÑ, ÑÑо
       пÑÐ¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ñ Ð¿ÐµÑеÑÑÑанавливаÑÑ Ñвои
       желаемÑе пÑава клÑÑа заÑиÑÑ Ð¿Ñи вÑоде в
       обÑабоÑÑик Ñигнала, еÑли желаемÑе пÑава
       оÑлиÑаÑÑÑÑ Ð¾Ñ Ð·Ð½Ð°ÑÐµÐ½Ð¸Ñ Ð¿Ð¾ ÑмолÑаниÑ. ÐÑава
       лÑбого пÑеÑванного конÑекÑÑа
       воÑÑÑанавливаÑÑÑÑ Ð¿Ñи завеÑÑении
       обÑабоÑÑика Ñигналов.

       Ðанное поведение Ñигнала необÑÑно из-за
       Ñого, ÑÑо ÑегиÑÑÑ x86 PKRU (коÑоÑÑй ÑÑÐ°Ð½Ð¸Ñ Ð¿Ñава
       доÑÑÑпа клÑÑа заÑиÑÑ) ÑпÑавлÑеÑÑÑ Ñем же
       аппаÑаÑнÑм меÑанизмом (XSAVE) ÑÑо и ÑегиÑÑÑÑ
       плаваÑÑей запÑÑой. Ðоведение Ñигнала
       Ñакое же как Ñ ÑегиÑÑÑов плаваÑÑей запÑÑой.

   СиÑÑемнÑе вÑÐ·Ð¾Ð²Ñ ÐºÐ»ÑÑей заÑиÑÑ
       Ð ÑдÑе Linux ÑÐµÐ°Ð»Ð¸Ð·Ð¾Ð²Ð°Ð½Ñ ÑледÑÑÑие ÑиÑÑемнÑе
       вÑÐ·Ð¾Ð²Ñ Ð´Ð»Ñ ÑабоÑÑ Ñ pkey: pkey_mprotect(2), pkey_alloc(2) и
       pkey_free(2).

       СиÑÑемнÑе вÑÐ·Ð¾Ð²Ñ Linux pkey доÑÑÑÐ¿Ð½Ñ ÑолÑко, еÑли
       ÑдÑо бÑло ÑобÑано Ñ Ð²ÐºÐ»ÑÑÑннÑм паÑамеÑÑом
       CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS.

ÐÐ ÐÐÐÐ
       ÐÑогÑамма, пÑедÑÑÐ°Ð²Ð»ÐµÐ½Ð½Ð°Ñ Ð´Ð°Ð»ÐµÐµ, вÑделÑеÑ
       ÑÑÑаниÑÑ Ð¿Ð°Ð¼ÑÑи Ñ Ð¿Ñавами на ÑÑение и запиÑÑ.
       ÐаÑем она запиÑÑÐ²Ð°ÐµÑ ÐºÑÑок даннÑÑ Ð² памÑÑи и
       ÑиÑÐ°ÐµÑ ÐµÐ³Ð¾. ÐоÑле ÑÑого она пÑÑаеÑÑÑ Ð²ÑделиÑÑ
       клÑÑ Ð·Ð°ÑиÑÑ Ð¸ запÑеÑиÑÑ Ð´Ð¾ÑÑÑп к ÑÑÑаниÑе Ñ
       помоÑÑÑ Ð¸Ð½ÑÑÑÑкÑии WRPKRU. Ðалее она пÑÑаеÑÑÑ
       полÑÑиÑÑ Ð´Ð¾ÑÑÑп к ÑÑÑаниÑе, ÑÑо, как мÑ
       ожидаем, вÑÐ·Ð¾Ð²ÐµÑ Ñигнал завеÑÑениÑ
       пÑиложениÑ.

           $ ./a.out
           бÑÑÐµÑ ÑодеÑжиÑ: 73
           ÑиÑаем бÑÑÐµÑ Ñнова...
           Segmentation fault (core dumped)

   ÐÑÑоднÑй код пÑогÑаммÑ

       #define _GNU_SOURCE
       #include <unistd.h>
       #include <sys/syscall.h>
       #include <stdio.h>
       #include <sys/mman.h>

       static inline void
       wrpkru(unsigned int pkru)
       {
           unsigned int eax = pkru;
           unsigned int ecx = 0;
           unsigned int edx = 0;

           asm volatile(".byte 0x0f,0x01,0xef\n\t"
                        : : "a" (eax), "c" (ecx), "d" (edx));
       }

       int
       pkey_set(int pkey, unsigned long rights, unsigned long flags)
       {
           unsigned int pkru = (rights << (2 * pkey));
           return wrpkru(pkru);
       }

       int
       pkey_mprotect(void *ptr, size_t size, unsigned long orig_prot,
                     unsigned long pkey)
       {
           return syscall(SYS_pkey_mprotect, ptr, size, orig_prot, pkey);
       }

       int
       pkey_alloc(void)
       {
           return syscall(SYS_pkey_alloc, 0, 0);
       }

       int
       pkey_free(unsigned long pkey)
       {
           return syscall(SYS_pkey_free, pkey);
       }

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                                  } while (0)

       int
       main(void)
       {
           int status;
           int pkey;
           int *buffer;

           /*
            * вÑделÑем ÑÑÑаниÑÑ Ð¿Ð°Ð¼ÑÑи
            */
           buffer = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
           if (buffer == MAP_FAILED)
               errExit("mmap");

           /*
            * пиÑем пÑоизволÑнÑе даннÑе в ÑÑÑаниÑÑ (ÑÑÑÑ)
            */
           *buffer = __LINE__;
           printf("бÑÑÐµÑ ÑодеÑжиÑ: %d\n", *buffer);

           /*
            * вÑделÑем клÑÑ Ð·Ð°ÑиÑÑ:
            */
           pkey = pkey_alloc();
           if (pkey == -1)
               errExit("pkey_alloc");

           /*
            * запÑеÑаем доÑÑÑп к памÑÑи, на коÑоÑой бÑÐ´ÐµÑ ÑÑÑановлен «pkey»,
            * ÑоÑÑ Ð¿Ð¾ÐºÐ° ниÑего не запÑеÑено
            */
           status = pkey_set(pkey, PKEY_DISABLE_ACCESS, 0);
           if (status)
               errExit("pkey_set");

           /*
            * ÑÑÑановим клÑÑ Ð·Ð°ÑиÑÑ Ð½Ð° «бÑÑеÑ»
            * замеÑим, ÑÑо он доÑÑÑпен пока не пÑименÑн mprotect()
            * и клÑÑ Ð½Ðµ заменен ÑозданнÑм Ñанее pkey_set()
            */
           status = pkey_mprotect(buffer, getpagesize(),
                                  PROT_READ | PROT_WRITE, pkey);
           if (status == -1)
               errExit("pkey_mprotect");

           printf("ÑиÑаем бÑÑÐµÑ Ñнова...\n");

           /*
            * пÑиложение падаеÑ, Ñак как Ð¼Ñ Ð·Ð°Ð¿ÑеÑили доÑÑÑп
            */
           printf("бÑÑÐµÑ ÑодеÑжиÑ: %d\n", *buffer);

           status = pkey_free(pkey);
           if (status == -1)
               errExit("pkey_free");

           exit(EXIT_SUCCESS);
       }

СÐÐТРÐТРТÐÐÐÐ
       pkey_alloc(2), pkey_free(2), pkey_mprotect(2), sigaction(2)



Linux                             2017-09-15                          PKEYS(7)