smap.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #include "libcflat.h"
  2. #include "x86/desc.h"
  3. #include "x86/processor.h"
  4. #include "x86/vm.h"
  5. #define X86_FEATURE_SMAP 20
  6. volatile int pf_count = 0;
  7. volatile int save;
  8. volatile unsigned test;
  9. // When doing ring 3 tests, page fault handlers will always run on a
  10. // separate stack (the ring 0 stack). Seems easier to use the alt_stack
  11. // mechanism for both ring 0 and ring 3.
  12. void do_pf_tss(unsigned long error_code)
  13. {
  14. pf_count++;
  15. save = test;
  16. #ifndef __x86_64__
  17. tss.eflags |= X86_EFLAGS_AC;
  18. #endif
  19. }
  20. extern void pf_tss(void);
  21. asm ("pf_tss:\n"
  22. #ifdef __x86_64__
  23. // no task on x86_64, save/restore caller-save regs
  24. "push %rax; push %rcx; push %rdx; push %rsi; push %rdi\n"
  25. "push %r8; push %r9; push %r10; push %r11\n"
  26. "mov 9*8(%rsp),%rsi\n"
  27. #endif
  28. "call do_pf_tss\n"
  29. #ifdef __x86_64__
  30. "pop %r11; pop %r10; pop %r9; pop %r8\n"
  31. "pop %rdi; pop %rsi; pop %rdx; pop %rcx; pop %rax\n"
  32. #endif
  33. "add $"S", %"R "sp\n"
  34. #ifdef __x86_64__
  35. "orl $" xstr(X86_EFLAGS_AC) ", 2*"S"(%"R "sp)\n" // set EFLAGS.AC and retry
  36. #endif
  37. "iret"W" \n\t"
  38. "jmp pf_tss\n\t");
  39. #define USER_BASE (1 << 24)
  40. #define USER_VAR(v) (*((__typeof__(&(v))) (((unsigned long)&v) + USER_BASE)))
  41. #define USER_ADDR(v) ((void *)((unsigned long)(&v) + USER_BASE))
  42. static void init_test(int i)
  43. {
  44. pf_count = 0;
  45. if (i) {
  46. invlpg(&test);
  47. invlpg(&USER_VAR(test));
  48. }
  49. }
  50. static void check_smap_nowp(void)
  51. {
  52. test = 0x99;
  53. *get_pte(phys_to_virt(read_cr3()), USER_ADDR(test)) &= ~PT_WRITABLE_MASK;
  54. write_cr4(read_cr4() & ~X86_CR4_SMAP);
  55. write_cr0(read_cr0() & ~X86_CR0_WP);
  56. clac();
  57. write_cr3(read_cr3());
  58. init_test(0);
  59. USER_VAR(test) = 0x99;
  60. report("write from user page with SMAP=0, AC=0, WP=0, PTE.U=1 && PTE.W=0", pf_count == 0);
  61. write_cr4(read_cr4() | X86_CR4_SMAP);
  62. write_cr3(read_cr3());
  63. init_test(0);
  64. (void)USER_VAR(test);
  65. report("read from user page with SMAP=1, AC=0, WP=0, PTE.U=1 && PTE.W=0", pf_count == 1 && save == 0x99);
  66. /* Undo changes */
  67. *get_pte(phys_to_virt(read_cr3()), USER_ADDR(test)) |= PT_WRITABLE_MASK;
  68. write_cr0(read_cr0() | X86_CR0_WP);
  69. write_cr3(read_cr3());
  70. }
  71. int main(int ac, char **av)
  72. {
  73. unsigned long i;
  74. if (!(cpuid_indexed(7, 0).b & (1 << X86_FEATURE_SMAP))) {
  75. printf("SMAP not enabled\n");
  76. return report_summary();
  77. }
  78. setup_vm();
  79. setup_alt_stack();
  80. set_intr_alt_stack(14, pf_tss);
  81. // Map first 16MB as supervisor pages
  82. for (i = 0; i < USER_BASE; i += PAGE_SIZE) {
  83. *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~PT_USER_MASK;
  84. invlpg((void *)i);
  85. }
  86. // Present the same 16MB as user pages in the 16MB-32MB range
  87. for (i = USER_BASE; i < 2 * USER_BASE; i += PAGE_SIZE) {
  88. *get_pte(phys_to_virt(read_cr3()), phys_to_virt(i)) &= ~USER_BASE;
  89. invlpg((void *)i);
  90. }
  91. clac();
  92. write_cr4(read_cr4() | X86_CR4_SMAP);
  93. write_cr3(read_cr3());
  94. for (i = 0; i < 2; i++) {
  95. if (i)
  96. printf("testing with INVLPG\n");
  97. else
  98. printf("testing without INVLPG\n");
  99. init_test(i);
  100. clac();
  101. test = 42;
  102. report("write to supervisor page", pf_count == 0 && test == 42);
  103. init_test(i);
  104. stac();
  105. (void)USER_VAR(test);
  106. report("read from user page with AC=1", pf_count == 0);
  107. init_test(i);
  108. clac();
  109. (void)USER_VAR(test);
  110. report("read from user page with AC=0", pf_count == 1 && save == 42);
  111. init_test(i);
  112. stac();
  113. save = 0;
  114. USER_VAR(test) = 43;
  115. report("write to user page with AC=1", pf_count == 0 && test == 43);
  116. init_test(i);
  117. clac();
  118. USER_VAR(test) = 44;
  119. report("read from user page with AC=0", pf_count == 1 && test == 44 && save == 43);
  120. init_test(i);
  121. stac();
  122. test = -1;
  123. asm("or $(" xstr(USER_BASE) "), %"R "sp \n"
  124. "push $44 \n "
  125. "decl test\n"
  126. "and $~(" xstr(USER_BASE) "), %"R "sp \n"
  127. "pop %"R "ax\n"
  128. "movl %eax, test");
  129. report("write to user stack with AC=1", pf_count == 0 && test == 44);
  130. init_test(i);
  131. clac();
  132. test = -1;
  133. asm("or $(" xstr(USER_BASE) "), %"R "sp \n"
  134. "push $45 \n "
  135. "decl test\n"
  136. "and $~(" xstr(USER_BASE) "), %"R "sp \n"
  137. "pop %"R "ax\n"
  138. "movl %eax, test");
  139. report("write to user stack with AC=0", pf_count == 1 && test == 45 && save == -1);
  140. /* This would be trapped by SMEP */
  141. init_test(i);
  142. clac();
  143. asm("jmp 1f + "xstr(USER_BASE)" \n"
  144. "1: jmp 2f - "xstr(USER_BASE)" \n"
  145. "2:");
  146. report("executing on user page with AC=0", pf_count == 0);
  147. }
  148. check_smap_nowp();
  149. // TODO: implicit kernel access from ring 3 (e.g. int)
  150. return report_summary();
  151. }