Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/pkeys: Preallocate execute-only key

execute-only key is allocated dynamically. This is a problem. When a
thread implicitly creates an execute-only key, and resets the UAMOR
for that key, the UAMOR value does not percolate to all the other
threads. Any other thread may ignorantly change the permissions on the
key. This can cause the key to be not execute-only for that thread.

Preallocate the execute-only key and ensure that no thread can change
the permission of the key, by resetting the corresponding bit in
UAMOR.

Fixes: 5586cf61e108 ("powerpc: introduce execute-only pkey")
Cc: stable@vger.kernel.org # v4.16+
Signed-off-by: Ram Pai <linuxram@us.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Ram Pai and committed by
Michael Ellerman
a4fcc877 fe6a2804

+18 -45
+18 -45
arch/powerpc/mm/pkeys.c
··· 18 18 u64 pkey_amr_mask; /* Bits in AMR not to be touched */ 19 19 u64 pkey_iamr_mask; /* Bits in AMR not to be touched */ 20 20 u64 pkey_uamor_mask; /* Bits in UMOR not to be touched */ 21 + int execute_only_key = 2; 21 22 22 23 #define AMR_BITS_PER_PKEY 2 23 24 #define AMR_RD_BIT 0x1UL ··· 121 120 #else 122 121 os_reserved = 0; 123 122 #endif 124 - initial_allocation_mask = (0x1 << 0) | (0x1 << 1); 123 + initial_allocation_mask = (0x1 << 0) | (0x1 << 1) | 124 + (0x1 << execute_only_key); 125 125 126 126 /* register mask is in BE format */ 127 127 pkey_amr_mask = ~0x0ul; ··· 130 128 131 129 pkey_iamr_mask = ~0x0ul; 132 130 pkey_iamr_mask &= ~(0x3ul << pkeyshift(0)); 131 + pkey_iamr_mask &= ~(0x3ul << pkeyshift(execute_only_key)); 133 132 134 133 pkey_uamor_mask = ~0x0ul; 135 134 pkey_uamor_mask &= ~(0x3ul << pkeyshift(0)); 135 + pkey_uamor_mask &= ~(0x3ul << pkeyshift(execute_only_key)); 136 136 137 137 /* mark the rest of the keys as reserved and hence unavailable */ 138 138 for (i = (pkeys_total - os_reserved); i < pkeys_total; i++) { 139 139 initial_allocation_mask |= (0x1 << i); 140 140 pkey_uamor_mask &= ~(0x3ul << pkeyshift(i)); 141 + } 142 + 143 + if (unlikely((pkeys_total - os_reserved) <= execute_only_key)) { 144 + /* 145 + * Insufficient number of keys to support 146 + * execute only key. Mark it unavailable. 147 + * Any AMR, UAMOR, IAMR bit set for 148 + * this key is irrelevant since this key 149 + * can never be allocated. 150 + */ 151 + execute_only_key = -1; 141 152 } 142 153 143 154 return 0; ··· 163 148 if (static_branch_likely(&pkey_disabled)) 164 149 return; 165 150 mm_pkey_allocation_map(mm) = initial_allocation_mask; 166 - /* -1 means unallocated or invalid */ 167 - mm->context.execute_only_pkey = -1; 151 + mm->context.execute_only_pkey = execute_only_key; 168 152 } 169 153 170 154 static inline u64 read_amr(void) ··· 315 301 316 302 int __execute_only_pkey(struct mm_struct *mm) 317 303 { 318 - bool need_to_set_mm_pkey = false; 319 - int execute_only_pkey = mm->context.execute_only_pkey; 320 - int ret; 321 - 322 - /* Do we need to assign a pkey for mm's execute-only maps? */ 323 - if (execute_only_pkey == -1) { 324 - /* Go allocate one to use, which might fail */ 325 - execute_only_pkey = mm_pkey_alloc(mm); 326 - if (execute_only_pkey < 0) 327 - return -1; 328 - need_to_set_mm_pkey = true; 329 - } 330 - 331 - /* 332 - * We do not want to go through the relatively costly dance to set AMR 333 - * if we do not need to. Check it first and assume that if the 334 - * execute-only pkey is readwrite-disabled than we do not have to set it 335 - * ourselves. 336 - */ 337 - if (!need_to_set_mm_pkey && !pkey_allows_readwrite(execute_only_pkey)) 338 - return execute_only_pkey; 339 - 340 - /* 341 - * Set up AMR so that it denies access for everything other than 342 - * execution. 343 - */ 344 - ret = __arch_set_user_pkey_access(current, execute_only_pkey, 345 - PKEY_DISABLE_ACCESS | 346 - PKEY_DISABLE_WRITE); 347 - /* 348 - * If the AMR-set operation failed somehow, just return 0 and 349 - * effectively disable execute-only support. 350 - */ 351 - if (ret) { 352 - mm_pkey_free(mm, execute_only_pkey); 353 - return -1; 354 - } 355 - 356 - /* We got one, store it and use it from here on out */ 357 - if (need_to_set_mm_pkey) 358 - mm->context.execute_only_pkey = execute_only_pkey; 359 - return execute_only_pkey; 304 + return mm->context.execute_only_pkey; 360 305 } 361 306 362 307 static inline bool vma_is_pkey_exec_only(struct vm_area_struct *vma)