In a multithreaded application, a key allocated by one thread must be activate and usable on all threads.
Currently this is not the case, because the UAMOR bits for all keys are disabled by default. When a new key is allocated in one thread, though the corresponding UAMOR bits for that thread get enabled, the UAMOR bits for all other existing threads continue to have their bits disabled. Other threads have no way to set permissions on the key, effectively making the key useless. Enable the UAMOR bits for all keys, at process creation. Since the contents of UAMOR are inherited at fork, all threads are capable of modifying the permissions on any key. BTW: changing the permission on unallocated keys has no effect, till those keys are not associated with any PTEs. The kernel will anyway disallow to association of unallocated keys with PTEs. CC: Andy Lutomirski <l...@kernel.org> CC: Florian Weimer <fwei...@redhat.com> CC: Thiago Jung Bauermann <bauer...@linux.ibm.com> CC: Michael Ellerman <m...@ellerman.id.au> Signed-off-by: Ram Pai <linux...@us.ibm.com> --- arch/powerpc/mm/pkeys.c | 40 ++++++++++++++++++++++------------------ 1 files changed, 22 insertions(+), 18 deletions(-) diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c index e6f500f..6529f4e 100644 --- a/arch/powerpc/mm/pkeys.c +++ b/arch/powerpc/mm/pkeys.c @@ -15,8 +15,9 @@ int pkeys_total; /* Total pkeys as per device tree */ bool pkeys_devtree_defined; /* pkey property exported by device tree */ u32 initial_allocation_mask; /* Bits set for reserved keys */ -u64 pkey_amr_uamor_mask; /* Bits in AMR/UMOR not to be touched */ +u64 pkey_amr_mask; /* Bits in AMR not to be touched */ u64 pkey_iamr_mask; /* Bits in AMR not to be touched */ +u64 pkey_uamor_mask; /* Bits in UMOR not to be touched */ #define AMR_BITS_PER_PKEY 2 #define AMR_RD_BIT 0x1UL @@ -119,20 +120,22 @@ int pkey_initialize(void) #else os_reserved = 0; #endif - initial_allocation_mask = ~0x0; - pkey_amr_uamor_mask = ~0x0ul; + initial_allocation_mask = (0x1 << 0) | (0x1 << 1); + + /* register mask is in BE format */ + pkey_amr_mask = ~0x0ul; pkey_iamr_mask = ~0x0ul; - /* - * key 0, 1 are reserved. - * key 0 is the default key, which allows read/write/execute. - * key 1 is recommended not to be used. PowerISA(3.0) page 1015, - * programming note. - */ - for (i = 2; i < (pkeys_total - os_reserved); i++) { - initial_allocation_mask &= ~(0x1 << i); - pkey_amr_uamor_mask &= ~(0x3ul << pkeyshift(i)); + + for (i = 0; i < (pkeys_total - os_reserved); i++) { + pkey_amr_mask &= ~(0x3ul << pkeyshift(i)); pkey_iamr_mask &= ~(0x1ul << pkeyshift(i)); } + + pkey_uamor_mask = ~0x0ul; + pkey_uamor_mask &= ~(0x3ul << pkeyshift(0)); + for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) + pkey_uamor_mask &= ~(0x3ul << pkeyshift(i)); + return 0; } @@ -289,9 +292,6 @@ void thread_pkey_regs_restore(struct thread_struct *new_thread, if (static_branch_likely(&pkey_disabled)) return; - /* - * TODO: Just set UAMOR to zero if @new_thread hasn't used any keys yet. - */ if (old_thread->amr != new_thread->amr) write_amr(new_thread->amr); if (old_thread->iamr != new_thread->iamr) @@ -305,9 +305,13 @@ void thread_pkey_regs_init(struct thread_struct *thread) if (static_branch_likely(&pkey_disabled)) return; - thread->amr = read_amr() & pkey_amr_uamor_mask; - thread->iamr = read_iamr() & pkey_iamr_mask; - thread->uamor = read_uamor() & pkey_amr_uamor_mask; + thread->amr = pkey_amr_mask; + thread->iamr = pkey_iamr_mask; + thread->uamor = pkey_uamor_mask; + + write_uamor(pkey_uamor_mask); + write_amr(pkey_amr_mask); + write_iamr(pkey_iamr_mask); } static inline bool pkey_allows_readwrite(int pkey) -- 1.7.1