Skip to contentSkip to conversation

powerpc/pkeys: copy pkey-tracking-information at fork()

PATCH Superseded Ram Pai 5 participants 8 messages Dec 3, 2018 – Dec 23, 2018 PowerPC

lore
Share & tools
lore

Ram Pai

author
#0 lore raw

PATCH 1/1 powerpc/pkeys: copy pkey-tracking-information at fork()

At fork(), the pkey tracking information is not copied over to the mm_struct of the child. This can cause the child to erroneously allocate keys that were already allocated. Any allocated execute-only key is lost aswell.

Add code; called by dup_mmap(), to copy the pkey state from parent to child explicitly.

This problem was originally found by Dave Hansen on x86, which turns out to be a problem on powerpc aswell.

Testing:
Passes the new selftest added by Dave Hansen. I have temporarily captured that selftest at
https://github.com/rampai/memorykeys/ -b memkey.v15-rc6

---
 arch/powerpc/include/asm/mmu_context.h |   15 +++++++++------
 arch/powerpc/mm/pkeys.c                |    7 +++++++
 2 files changed, 16 insertions(+), 6 deletions(-)
arch/powerpc/include/asm/mmu_context.h +9 -6
index 0381394..cb16146 100644
@@ -217,12 +217,6 @@ static inline void enter_lazy_tlb(struct mm_struct *mm,
 #endif
 }
 
-static inline int arch_dup_mmap(struct mm_struct *oldmm,
-				struct mm_struct *mm)
-{
-	return 0;
-}
-
 #ifndef CONFIG_PPC_BOOK3S_64
 static inline void arch_exit_mmap(struct mm_struct *mm)
 {
@@ -247,6 +241,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
 #ifdef CONFIG_PPC_MEM_KEYS
 bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 			       bool execute, bool foreign);
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm);
 #else /* CONFIG_PPC_MEM_KEYS */
 static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 		bool write, bool execute, bool foreign)
@@ -259,6 +254,7 @@ static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 #define thread_pkey_regs_save(thread)
 #define thread_pkey_regs_restore(new_thread, old_thread)
 #define thread_pkey_regs_init(thread)
+#define arch_dup_pkeys(oldmm, mm)
 
 static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 {
@@ -267,5 +263,12 @@ static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 
 #endif /* CONFIG_PPC_MEM_KEYS */
 
+static inline int arch_dup_mmap(struct mm_struct *oldmm,
+				struct mm_struct *mm)
+{
+	arch_dup_pkeys(oldmm, mm);
+	return 0;
+}
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_POWERPC_MMU_CONTEXT_H */
arch/powerpc/mm/pkeys.c +7 -0
index b271b28..5d65c47 100644
@@ -414,3 +414,10 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 	return pkey_access_permitted(vma_pkey(vma), write, execute);
 }
+
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}
Signature · 1 line
1.7.1
Reviewed-by on v1 1/1
#1 lore raw
Quoted 73 lines · Ram Pai

At fork(), the pkey tracking information is not copied over to the mm_struct of the child. This can cause the child to erroneously allocate keys that were already allocated. Any allocated execute-only key is lost aswell.

Add code; called by dup_mmap(), to copy the pkey state from parent to child explicitly.

This problem was originally found by Dave Hansen on x86, which turns out to be a problem on powerpc aswell.

Testing:
Passes the new selftest added by Dave Hansen. I have temporarily captured that selftest at
https://github.com/rampai/memorykeys/ -b memkey.v15-rc6

---
 arch/powerpc/include/asm/mmu_context.h |   15 +++++++++------
 arch/powerpc/mm/pkeys.c                |    7 +++++++
 2 files changed, 16 insertions(+), 6 deletions(-)
arch/powerpc/include/asm/mmu_context.h +9 -6
index 0381394..cb16146 100644
@@ -217,12 +217,6 @@ static inline void enter_lazy_tlb(struct mm_struct *mm,
 #endif
 }
 
-static inline int arch_dup_mmap(struct mm_struct *oldmm,
-				struct mm_struct *mm)
-{
-	return 0;
-}
-
 #ifndef CONFIG_PPC_BOOK3S_64
 static inline void arch_exit_mmap(struct mm_struct *mm)
 {
@@ -247,6 +241,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
 #ifdef CONFIG_PPC_MEM_KEYS
 bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 			       bool execute, bool foreign);
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm);
 #else /* CONFIG_PPC_MEM_KEYS */
 static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 		bool write, bool execute, bool foreign)
@@ -259,6 +254,7 @@ static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 #define thread_pkey_regs_save(thread)
 #define thread_pkey_regs_restore(new_thread, old_thread)
 #define thread_pkey_regs_init(thread)
+#define arch_dup_pkeys(oldmm, mm)
 
 static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 {
@@ -267,5 +263,12 @@ static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 
 #endif /* CONFIG_PPC_MEM_KEYS */
 
+static inline int arch_dup_mmap(struct mm_struct *oldmm,
+				struct mm_struct *mm)
+{
+	arch_dup_pkeys(oldmm, mm);
+	return 0;
+}
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_POWERPC_MMU_CONTEXT_H */
arch/powerpc/mm/pkeys.c +0 -0
index b271b28..5d65c47 100644

arch/powerpc/mm/pkeys.c bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, · line 423

Quoted 10 lines
arch/powerpc/mm/pkeys.c +7 -0
@@ -414,3 +414,10 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 	return pkey_access_permitted(vma_pkey(vma), write, execute);
 }
+
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}

This function is small and perhaps could have been a static inline in <asm/mmu_context.h>, but arch_dup_mmap() doesn't seem to be in a hot path so the segregation of implementation details to pkeys.c is nice from an organization point of view.

--
Thiago Jung Bauermann
IBM Linux Technology Center

#2 lore raw
In reply to Ram Pai

* Ram Pai:

arch/powerpc/mm/pkeys.c bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, · line 423

Quoted 6 lines
arch/powerpc/mm/pkeys.c +6 -0
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}

Looks reasonable to me.

Thanks,
Florian (who is not a kernel developer though)

#3 lore raw
In reply to Ram Pai

Hi Ram,

Thanks for fixing this.

Quoted 5 lines · Ram Pai
arch/powerpc/mm/pkeys.c +0 -0
index b271b28..5d65c47 100644

arch/powerpc/mm/pkeys.c bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, · line 423

Quoted 10 lines
arch/powerpc/mm/pkeys.c +7 -0
@@ -414,3 +414,10 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 	return pkey_access_permitted(vma_pkey(vma), write, execute);
 }
+
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}

Shouldn't this check if pkeys are actually in use?

eg:

arch/powerpc/mm/pkeys.c +3 -0
index cf87dddefbdc..587807763737 100644
@@ -418,6 +418,9 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
 {
+	if (static_branch_likely(&pkey_disabled))
+		return;
+
 	/* Duplicate the oldmm pkey state in mm: */
 	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
 	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;

Ideally we'd actually do it in the inline so that the function call to arch_dup_pkeys() can be avoided. But it looks like header dependencies might make that hard.

cheers

author
#4 lore raw
Quoted 11 lines · Michael Ellerman

Hi Ram,

Thanks for fixing this.

Quoted 6 lines · Ram Pai
arch/powerpc/mm/pkeys.c +0 -0
index b271b28..5d65c47 100644
@@ -414,3 +414,10 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
Quoted 11 lines

arch/powerpc/mm/pkeys.c bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write, · line 423

Quoted 9 lines
arch/powerpc/mm/pkeys.c +7 -0
 	return pkey_access_permitted(vma_pkey(vma), write, execute);
 }
+
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}

Shouldn't this check if pkeys are actually in use?

Yes. That will make it better. However not checking it, will not hurt. Just that it will do some unnecessary copying.

Will spin out a new patch with the fix, and send it your way. RP

Quoted 24 lines

eg:

arch/powerpc/mm/pkeys.c +3 -0
index cf87dddefbdc..587807763737 100644
@@ -418,6 +418,9 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
 {
+	if (static_branch_likely(&pkey_disabled))
+		return;
+
 	/* Duplicate the oldmm pkey state in mm: */
 	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
 	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;

Ideally we'd actually do it in the inline so that the function call to arch_dup_pkeys() can be avoided. But it looks like header dependencies might make that hard.

cheers

Signature · 1 line
Ram Pai
author
#5 lore raw

PATCH v2 1/1 powerpc/pkeys: copy pkey-tracking-information at fork()

In reply to Michael Ellerman

Pkey tracking information is not copied over to the mm_struct of the child during fork(). This can cause the child to erroneously allocate keys that were already allocated. Any allocated execute-only key is lost aswell.

Add code; called by dup_mmap(), to copy the pkey state from parent to child explicitly.

This problem was originally found by Dave Hansen on x86, which turns out to be a problem on powerpc aswell.

v2: do not copy if pkeys is disabled.
-- comment by Michael Ellermen

arch/powerpc/include/asm/mmu_context.h +9 -6
index 0381394..cb16146 100644
@@ -217,12 +217,6 @@ static inline void enter_lazy_tlb(struct mm_struct *mm,
 #endif
 }
 
-static inline int arch_dup_mmap(struct mm_struct *oldmm,
-				struct mm_struct *mm)
-{
-	return 0;
-}
-
 #ifndef CONFIG_PPC_BOOK3S_64
 static inline void arch_exit_mmap(struct mm_struct *mm)
 {
@@ -247,6 +241,7 @@ static inline void arch_bprm_mm_init(struct mm_struct *mm,
 #ifdef CONFIG_PPC_MEM_KEYS
 bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 			       bool execute, bool foreign);
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm);
 #else /* CONFIG_PPC_MEM_KEYS */
 static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 		bool write, bool execute, bool foreign)
@@ -259,6 +254,7 @@ static inline bool arch_vma_access_permitted(struct vm_area_struct *vma,
 #define thread_pkey_regs_save(thread)
 #define thread_pkey_regs_restore(new_thread, old_thread)
 #define thread_pkey_regs_init(thread)
+#define arch_dup_pkeys(oldmm, mm)
 
 static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 {
@@ -267,5 +263,12 @@ static inline u64 pte_to_hpte_pkey_bits(u64 pteflags)
 
 #endif /* CONFIG_PPC_MEM_KEYS */
 
+static inline int arch_dup_mmap(struct mm_struct *oldmm,
+				struct mm_struct *mm)
+{
+	arch_dup_pkeys(oldmm, mm);
+	return 0;
+}
+
 #endif /* __KERNEL__ */
 #endif /* __ASM_POWERPC_MMU_CONTEXT_H */
arch/powerpc/mm/pkeys.c +10 -0
index b271b28..25a8dd9 100644
@@ -414,3 +414,13 @@ bool arch_vma_access_permitted(struct vm_area_struct *vma, bool write,
 
 	return pkey_access_permitted(vma_pkey(vma), write, execute);
 }
+
+void arch_dup_pkeys(struct mm_struct *oldmm, struct mm_struct *mm)
+{
+	if (static_branch_likely(&pkey_disabled))
+		return;
+
+	/* Duplicate the oldmm pkey state in mm: */
+	mm_pkey_allocation_map(mm) = mm_pkey_allocation_map(oldmm);
+	mm->context.execute_only_pkey = oldmm->context.execute_only_pkey;
+}
#6 lore raw
Quoted 8 lines · Ram Pai

Pkey tracking information is not copied over to the mm_struct of the child during fork(). This can cause the child to erroneously allocate keys that were already allocated. Any allocated execute-only key is lost aswell.

Add code; called by dup_mmap(), to copy the pkey state from parent to child explicitly.

Quoted 8 lines

This problem was originally found by Dave Hansen on x86, which turns out to be a problem on powerpc aswell.

v2: do not copy if pkeys is disabled.
-- comment by Michael Ellermen

Thanks.

I changed the subject to:

powerpc/pkeys: Fix handling of pkey state across fork()

And added tags:

Fixes cf43d3b26452 ("powerpc: Enable pkey subsystem")
Cc [email protected] # v4.16+

cheers

#7 lore raw
In reply to Ram Pai
Quoted 5 lines · Ram Pai

Pkey tracking information is not copied over to the mm_struct of the child during fork(). This can cause the child to erroneously allocate keys that were already allocated. Any allocated execute-only key is lost aswell.

Quoted 8 lines

Add code; called by dup_mmap(), to copy the pkey state from parent to child explicitly.

This problem was originally found by Dave Hansen on x86, which turns out to be a problem on powerpc aswell.

Applied to powerpc next, thanks.

https://git.kernel.org/powerpc/c/2cd4bd192ee94848695c1c052d8791

cheers