0206-x86-pti-Map-the-vsyscall-page-if-needed.patch 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. From 7505dd405211a42c3abf52ef33b97eea470aaf60 Mon Sep 17 00:00:00 2001
  2. From: Andy Lutomirski <[email protected]>
  3. Date: Tue, 12 Dec 2017 07:56:42 -0800
  4. Subject: [PATCH 206/242] x86/pti: Map the vsyscall page if needed
  5. MIME-Version: 1.0
  6. Content-Type: text/plain; charset=UTF-8
  7. Content-Transfer-Encoding: 8bit
  8. CVE-2017-5754
  9. Make VSYSCALLs work fully in PTI mode by mapping them properly to the user
  10. space visible page tables.
  11. [ tglx: Hide unused functions (Patch by Arnd Bergmann) ]
  12. Signed-off-by: Andy Lutomirski <[email protected]>
  13. Signed-off-by: Thomas Gleixner <[email protected]>
  14. Cc: Borislav Petkov <[email protected]>
  15. Cc: Brian Gerst <[email protected]>
  16. Cc: Dave Hansen <[email protected]>
  17. Cc: David Laight <[email protected]>
  18. Cc: H. Peter Anvin <[email protected]>
  19. Cc: Josh Poimboeuf <[email protected]>
  20. Cc: Juergen Gross <[email protected]>
  21. Cc: Kees Cook <[email protected]>
  22. Cc: Linus Torvalds <[email protected]>
  23. Cc: Peter Zijlstra <[email protected]>
  24. Signed-off-by: Ingo Molnar <[email protected]>
  25. (cherry picked from commit 85900ea51577e31b186e523c8f4e068c79ecc7d3)
  26. Signed-off-by: Andy Whitcroft <[email protected]>
  27. Signed-off-by: Kleber Sacilotto de Souza <[email protected]>
  28. (cherry picked from commit 7a2ba0ea0a18cfc1f18c3f1389ef85f2a0d3227d)
  29. Signed-off-by: Fabian Grünbichler <[email protected]>
  30. ---
  31. arch/x86/include/asm/vsyscall.h | 1 +
  32. arch/x86/entry/vsyscall/vsyscall_64.c | 6 ++--
  33. arch/x86/mm/pti.c | 65 +++++++++++++++++++++++++++++++++++
  34. 3 files changed, 69 insertions(+), 3 deletions(-)
  35. diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
  36. index 6ba66ee79710..0eaeb223d692 100644
  37. --- a/arch/x86/include/asm/vsyscall.h
  38. +++ b/arch/x86/include/asm/vsyscall.h
  39. @@ -6,6 +6,7 @@
  40. #ifdef CONFIG_X86_VSYSCALL_EMULATION
  41. extern void map_vsyscall(void);
  42. +extern void set_vsyscall_pgtable_user_bits(pgd_t *root);
  43. /*
  44. * Called on instruction fetch fault in vsyscall page.
  45. diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
  46. index 5e56a4ced848..238b4bcd3c47 100644
  47. --- a/arch/x86/entry/vsyscall/vsyscall_64.c
  48. +++ b/arch/x86/entry/vsyscall/vsyscall_64.c
  49. @@ -343,14 +343,14 @@ int in_gate_area_no_mm(unsigned long addr)
  50. * vsyscalls but leave the page not present. If so, we skip calling
  51. * this.
  52. */
  53. -static void __init set_vsyscall_pgtable_user_bits(void)
  54. +void __init set_vsyscall_pgtable_user_bits(pgd_t *root)
  55. {
  56. pgd_t *pgd;
  57. p4d_t *p4d;
  58. pud_t *pud;
  59. pmd_t *pmd;
  60. - pgd = pgd_offset_k(VSYSCALL_ADDR);
  61. + pgd = pgd_offset_pgd(root, VSYSCALL_ADDR);
  62. set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
  63. p4d = p4d_offset(pgd, VSYSCALL_ADDR);
  64. #if CONFIG_PGTABLE_LEVELS >= 5
  65. @@ -372,7 +372,7 @@ void __init map_vsyscall(void)
  66. vsyscall_mode == NATIVE
  67. ? PAGE_KERNEL_VSYSCALL
  68. : PAGE_KERNEL_VVAR);
  69. - set_vsyscall_pgtable_user_bits();
  70. + set_vsyscall_pgtable_user_bits(swapper_pg_dir);
  71. }
  72. BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=
  73. diff --git a/arch/x86/mm/pti.c b/arch/x86/mm/pti.c
  74. index b1c38ef9fbbb..bce8aea65606 100644
  75. --- a/arch/x86/mm/pti.c
  76. +++ b/arch/x86/mm/pti.c
  77. @@ -38,6 +38,7 @@
  78. #include <asm/cpufeature.h>
  79. #include <asm/hypervisor.h>
  80. +#include <asm/vsyscall.h>
  81. #include <asm/cmdline.h>
  82. #include <asm/pti.h>
  83. #include <asm/pgtable.h>
  84. @@ -223,6 +224,69 @@ static pmd_t *pti_user_pagetable_walk_pmd(unsigned long address)
  85. return pmd_offset(pud, address);
  86. }
  87. +#ifdef CONFIG_X86_VSYSCALL_EMULATION
  88. +/*
  89. + * Walk the shadow copy of the page tables (optionally) trying to allocate
  90. + * page table pages on the way down. Does not support large pages.
  91. + *
  92. + * Note: this is only used when mapping *new* kernel data into the
  93. + * user/shadow page tables. It is never used for userspace data.
  94. + *
  95. + * Returns a pointer to a PTE on success, or NULL on failure.
  96. + */
  97. +static __init pte_t *pti_user_pagetable_walk_pte(unsigned long address)
  98. +{
  99. + gfp_t gfp = (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO);
  100. + pmd_t *pmd = pti_user_pagetable_walk_pmd(address);
  101. + pte_t *pte;
  102. +
  103. + /* We can't do anything sensible if we hit a large mapping. */
  104. + if (pmd_large(*pmd)) {
  105. + WARN_ON(1);
  106. + return NULL;
  107. + }
  108. +
  109. + if (pmd_none(*pmd)) {
  110. + unsigned long new_pte_page = __get_free_page(gfp);
  111. + if (!new_pte_page)
  112. + return NULL;
  113. +
  114. + if (pmd_none(*pmd)) {
  115. + set_pmd(pmd, __pmd(_KERNPG_TABLE | __pa(new_pte_page)));
  116. + new_pte_page = 0;
  117. + }
  118. + if (new_pte_page)
  119. + free_page(new_pte_page);
  120. + }
  121. +
  122. + pte = pte_offset_kernel(pmd, address);
  123. + if (pte_flags(*pte) & _PAGE_USER) {
  124. + WARN_ONCE(1, "attempt to walk to user pte\n");
  125. + return NULL;
  126. + }
  127. + return pte;
  128. +}
  129. +
  130. +static void __init pti_setup_vsyscall(void)
  131. +{
  132. + pte_t *pte, *target_pte;
  133. + unsigned int level;
  134. +
  135. + pte = lookup_address(VSYSCALL_ADDR, &level);
  136. + if (!pte || WARN_ON(level != PG_LEVEL_4K) || pte_none(*pte))
  137. + return;
  138. +
  139. + target_pte = pti_user_pagetable_walk_pte(VSYSCALL_ADDR);
  140. + if (WARN_ON(!target_pte))
  141. + return;
  142. +
  143. + *target_pte = *pte;
  144. + set_vsyscall_pgtable_user_bits(kernel_to_user_pgdp(swapper_pg_dir));
  145. +}
  146. +#else
  147. +static void __init pti_setup_vsyscall(void) { }
  148. +#endif
  149. +
  150. static void __init
  151. pti_clone_pmds(unsigned long start, unsigned long end, pmdval_t clear)
  152. {
  153. @@ -319,4 +383,5 @@ void __init pti_init(void)
  154. pti_clone_user_shared();
  155. pti_clone_entry_text();
  156. pti_setup_espfix64();
  157. + pti_setup_vsyscall();
  158. }
  159. --
  160. 2.14.2