powerpc: introduce execute-only pkey
This patch provides the implementation of execute-only pkey. The architecture-independent layer expects the arch-dependent layer, to support the ability to create and enable a special key which has execute-only permission. Acked-by: Balbir Singh <bsingharora@gmail.com> Signed-off-by: Ram Pai <linuxram@us.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
This commit is contained in:
		
							parent
							
								
									06bb53b338
								
							
						
					
					
						commit
						5586cf61e1
					
				| @ -116,6 +116,7 @@ typedef struct { | |||||||
| 	 * bit unset -> key available for allocation | 	 * bit unset -> key available for allocation | ||||||
| 	 */ | 	 */ | ||||||
| 	u32 pkey_allocation_map; | 	u32 pkey_allocation_map; | ||||||
|  | 	s16 execute_only_pkey; /* key holding execute-only protection */ | ||||||
| #endif | #endif | ||||||
| } mm_context_t; | } mm_context_t; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -117,9 +117,13 @@ static inline int mm_pkey_free(struct mm_struct *mm, int pkey) | |||||||
|  * Try to dedicate one of the protection keys to be used as an |  * Try to dedicate one of the protection keys to be used as an | ||||||
|  * execute-only protection key. |  * execute-only protection key. | ||||||
|  */ |  */ | ||||||
|  | extern int __execute_only_pkey(struct mm_struct *mm); | ||||||
| static inline int execute_only_pkey(struct mm_struct *mm) | static inline int execute_only_pkey(struct mm_struct *mm) | ||||||
| { | { | ||||||
| 	return 0; | 	if (static_branch_likely(&pkey_disabled)) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	return __execute_only_pkey(mm); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma, | static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma, | ||||||
|  | |||||||
| @ -92,6 +92,8 @@ void pkey_mm_init(struct mm_struct *mm) | |||||||
| 	if (static_branch_likely(&pkey_disabled)) | 	if (static_branch_likely(&pkey_disabled)) | ||||||
| 		return; | 		return; | ||||||
| 	mm_pkey_allocation_map(mm) = initial_allocation_mask; | 	mm_pkey_allocation_map(mm) = initial_allocation_mask; | ||||||
|  | 	/* -1 means unallocated or invalid */ | ||||||
|  | 	mm->context.execute_only_pkey = -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline u64 read_amr(void) | static inline u64 read_amr(void) | ||||||
| @ -256,3 +258,59 @@ void thread_pkey_regs_init(struct thread_struct *thread) | |||||||
| 	write_iamr(read_iamr() & pkey_iamr_mask); | 	write_iamr(read_iamr() & pkey_iamr_mask); | ||||||
| 	write_uamor(read_uamor() & pkey_amr_uamor_mask); | 	write_uamor(read_uamor() & pkey_amr_uamor_mask); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static inline bool pkey_allows_readwrite(int pkey) | ||||||
|  | { | ||||||
|  | 	int pkey_shift = pkeyshift(pkey); | ||||||
|  | 
 | ||||||
|  | 	if (!is_pkey_enabled(pkey)) | ||||||
|  | 		return true; | ||||||
|  | 
 | ||||||
|  | 	return !(read_amr() & ((AMR_RD_BIT|AMR_WR_BIT) << pkey_shift)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int __execute_only_pkey(struct mm_struct *mm) | ||||||
|  | { | ||||||
|  | 	bool need_to_set_mm_pkey = false; | ||||||
|  | 	int execute_only_pkey = mm->context.execute_only_pkey; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	/* Do we need to assign a pkey for mm's execute-only maps? */ | ||||||
|  | 	if (execute_only_pkey == -1) { | ||||||
|  | 		/* Go allocate one to use, which might fail */ | ||||||
|  | 		execute_only_pkey = mm_pkey_alloc(mm); | ||||||
|  | 		if (execute_only_pkey < 0) | ||||||
|  | 			return -1; | ||||||
|  | 		need_to_set_mm_pkey = true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We do not want to go through the relatively costly dance to set AMR | ||||||
|  | 	 * if we do not need to. Check it first and assume that if the | ||||||
|  | 	 * execute-only pkey is readwrite-disabled than we do not have to set it | ||||||
|  | 	 * ourselves. | ||||||
|  | 	 */ | ||||||
|  | 	if (!need_to_set_mm_pkey && !pkey_allows_readwrite(execute_only_pkey)) | ||||||
|  | 		return execute_only_pkey; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Set up AMR so that it denies access for everything other than | ||||||
|  | 	 * execution. | ||||||
|  | 	 */ | ||||||
|  | 	ret = __arch_set_user_pkey_access(current, execute_only_pkey, | ||||||
|  | 					  PKEY_DISABLE_ACCESS | | ||||||
|  | 					  PKEY_DISABLE_WRITE); | ||||||
|  | 	/*
 | ||||||
|  | 	 * If the AMR-set operation failed somehow, just return 0 and | ||||||
|  | 	 * effectively disable execute-only support. | ||||||
|  | 	 */ | ||||||
|  | 	if (ret) { | ||||||
|  | 		mm_pkey_free(mm, execute_only_pkey); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* We got one, store it and use it from here on out */ | ||||||
|  | 	if (need_to_set_mm_pkey) | ||||||
|  | 		mm->context.execute_only_pkey = execute_only_pkey; | ||||||
|  | 	return execute_only_pkey; | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user