Line data Source code
1 : #ifndef HEADER_fd_src_util_sandbox_fd_pkeys_h 2 : #define HEADER_fd_src_util_sandbox_fd_pkeys_h 3 : 4 : /* fd_pkeys.h provides APIs for userland memory protection keys. 5 : 6 : ### Protection Keys 7 : 8 : https://man7.org/linux/man-pages/man7/pkeys.7.html 9 : https://man7.org/linux/man-pages/man2/pkey_alloc.2.html 10 : https://man7.org/linux/man-pages/man2/pkey_mprotect.2.html 11 : 12 : Modern x86 and Arm CPUs allow labelling pages with a protection key 13 : (usually a 4 bit number). Using a special userland instruction, 14 : permission bits for such a key can then be modified very cheaply 15 : without kernel involvement. 16 : 17 : Example operation: 18 : - exec tile creates a pkey: pkey 3 19 : (using pkey_alloc(2)) 20 : - exec tile installs pkey 3 on the 'database' workspace 21 : (using fd_wksp_pkey_install, wrapping pkey_mprotect(2)) 22 : - whenever exec tile runs untrusted code (a user transaction), the 23 : exec tile sets pkey 3 to read-only, preventing stray writes to the 24 : database */ 25 : 26 : #include "../wksp/fd_wksp.h" 27 : 28 : #if defined(__linux__) 29 : 30 : FD_PROTOTYPES_BEGIN 31 : 32 : /* Syscall wrappers. Behavior matches glibc wrappers. */ 33 : 34 : int 35 : fd_syscall_pkey_alloc( uint flags, 36 : uint access_rights ); 37 : int 38 : fd_syscall_pkey_free( int pkey ); 39 : 40 : int 41 : fd_syscall_pkey_mprotect( void * addr, 42 : ulong size, 43 : int prot, 44 : int pkey ); 45 : 46 : /* Setup APIs. These are typically only used at startup. */ 47 : 48 : /* fd_wksp_pkey_install protects a workspace with a memory protection 49 : key. Wraps fd_shmem_pkey_install. Uses syscall pkey_mprotect. 50 : Returns 0 on success, errno otherwise. */ 51 : 52 : int 53 : fd_wksp_pkey_install( fd_wksp_t * wksp, 54 : int pkey ); 55 : 56 : /* fd_shmem_pkey_install protects an fd_shmem segment with a memory 57 : protection key. Uses syscall pkey_mprotect. Returns 0 on success, 58 : errno otherwise. */ 59 : 60 : int 61 : fd_shmem_pkey_install( fd_shmem_join_info_t const * join_info, 62 : int pkey ); 63 : 64 : FD_PROTOTYPES_END 65 : 66 : #endif /* defined(__linux__) */ 67 : 68 : /* APIs to update the PKRU register */ 69 : 70 : #if defined(__x86_64__) 71 : 72 : static inline ulong 73 0 : fd_x86_pkru_read( void ) { 74 0 : ulong pkru; 75 0 : __asm__ volatile ( 76 0 : "xor %%ecx, %%ecx\n" 77 0 : ".byte 0x0f,0x01,0xee\n" /* rdpkru */ 78 0 : : "=a" (pkru) 79 0 : : /* no inputs */ 80 0 : : "rcx", "rdx" /* rcx and rdx are clobbered */ 81 0 : ); 82 0 : return pkru; 83 0 : } 84 : 85 : static inline void 86 0 : fd_x86_pkru_write( ulong pkru ) { 87 0 : __asm__ volatile ( 88 0 : "xor %%ecx, %%ecx\n" 89 0 : "xor %%edx, %%edx\n" 90 0 : ".byte 0x0f,0x01,0xef\n" /* wrpkru */ 91 0 : : /* no outputs */ 92 0 : : "a" (pkru) 93 0 : : "rcx", "rdx" /* rcx and rdx are clobbered */ 94 0 : ); 95 0 : } 96 : 97 : static inline void 98 : fd_x86_pkey_update( int pkey, 99 : int access_disable, 100 0 : int write_disable ) { 101 0 : FD_TEST( pkey>=0 && pkey<16 ); /* x86 supports 16 pkeys (4 bits) */ 102 0 : int perm = (!!access_disable) | ((!!write_disable)<<1); 103 0 : int idx = pkey<<1; 104 0 : ulong pkru = fd_x86_pkru_read(); 105 0 : pkru &= ~(3UL<<idx); 106 0 : pkru |= ((ulong)perm)<<idx; 107 0 : fd_x86_pkru_write( pkru ); 108 0 : } 109 : 110 : #endif /* defined(__x86_64__) */ 111 : 112 : #endif /* HEADER_fd_src_util_sandbox_fd_pkeys_h */