iopl.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * iopl.c - Test case for a Linux on Xen 64-bit bug
  4. * Copyright (c) 2015 Andrew Lutomirski
  5. */
  6. #define _GNU_SOURCE
  7. #include <err.h>
  8. #include <stdio.h>
  9. #include <stdint.h>
  10. #include <signal.h>
  11. #include <setjmp.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <errno.h>
  15. #include <unistd.h>
  16. #include <sys/types.h>
  17. #include <sys/wait.h>
  18. #include <stdbool.h>
  19. #include <sched.h>
  20. #include <sys/io.h>
  21. #include "helpers.h"
  22. static int nerrs = 0;
  23. static jmp_buf jmpbuf;
  24. static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
  25. {
  26. siglongjmp(jmpbuf, 1);
  27. }
  28. static bool try_outb(unsigned short port)
  29. {
  30. sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
  31. if (sigsetjmp(jmpbuf, 1) != 0) {
  32. return false;
  33. } else {
  34. asm volatile ("outb %%al, %w[port]"
  35. : : [port] "Nd" (port), "a" (0));
  36. return true;
  37. }
  38. clearhandler(SIGSEGV);
  39. }
  40. static void expect_ok_outb(unsigned short port)
  41. {
  42. if (!try_outb(port)) {
  43. printf("[FAIL]\toutb to 0x%02hx failed\n", port);
  44. exit(1);
  45. }
  46. printf("[OK]\toutb to 0x%02hx worked\n", port);
  47. }
  48. static void expect_gp_outb(unsigned short port)
  49. {
  50. if (try_outb(port)) {
  51. printf("[FAIL]\toutb to 0x%02hx worked\n", port);
  52. nerrs++;
  53. }
  54. printf("[OK]\toutb to 0x%02hx failed\n", port);
  55. }
  56. #define RET_FAULTED 0
  57. #define RET_FAIL 1
  58. #define RET_EMUL 2
  59. static int try_cli(void)
  60. {
  61. unsigned long flags;
  62. sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
  63. if (sigsetjmp(jmpbuf, 1) != 0) {
  64. return RET_FAULTED;
  65. } else {
  66. asm volatile("cli; pushf; pop %[flags]"
  67. : [flags] "=rm" (flags));
  68. /* X86_FLAGS_IF */
  69. if (!(flags & (1 << 9)))
  70. return RET_FAIL;
  71. else
  72. return RET_EMUL;
  73. }
  74. clearhandler(SIGSEGV);
  75. }
  76. static int try_sti(bool irqs_off)
  77. {
  78. unsigned long flags;
  79. sethandler(SIGSEGV, sigsegv, SA_RESETHAND);
  80. if (sigsetjmp(jmpbuf, 1) != 0) {
  81. return RET_FAULTED;
  82. } else {
  83. asm volatile("sti; pushf; pop %[flags]"
  84. : [flags] "=rm" (flags));
  85. /* X86_FLAGS_IF */
  86. if (irqs_off && (flags & (1 << 9)))
  87. return RET_FAIL;
  88. else
  89. return RET_EMUL;
  90. }
  91. clearhandler(SIGSEGV);
  92. }
  93. static void expect_gp_sti(bool irqs_off)
  94. {
  95. int ret = try_sti(irqs_off);
  96. switch (ret) {
  97. case RET_FAULTED:
  98. printf("[OK]\tSTI faulted\n");
  99. break;
  100. case RET_EMUL:
  101. printf("[OK]\tSTI NOPped\n");
  102. break;
  103. default:
  104. printf("[FAIL]\tSTI worked\n");
  105. nerrs++;
  106. }
  107. }
  108. /*
  109. * Returns whether it managed to disable interrupts.
  110. */
  111. static bool test_cli(void)
  112. {
  113. int ret = try_cli();
  114. switch (ret) {
  115. case RET_FAULTED:
  116. printf("[OK]\tCLI faulted\n");
  117. break;
  118. case RET_EMUL:
  119. printf("[OK]\tCLI NOPped\n");
  120. break;
  121. default:
  122. printf("[FAIL]\tCLI worked\n");
  123. nerrs++;
  124. return true;
  125. }
  126. return false;
  127. }
  128. int main(void)
  129. {
  130. cpu_set_t cpuset;
  131. CPU_ZERO(&cpuset);
  132. CPU_SET(0, &cpuset);
  133. if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0)
  134. err(1, "sched_setaffinity to CPU 0");
  135. /* Probe for iopl support. Note that iopl(0) works even as nonroot. */
  136. switch(iopl(3)) {
  137. case 0:
  138. break;
  139. case -ENOSYS:
  140. printf("[OK]\tiopl() nor supported\n");
  141. return 0;
  142. default:
  143. printf("[OK]\tiopl(3) failed (%d) -- try running as root\n",
  144. errno);
  145. return 0;
  146. }
  147. /* Make sure that CLI/STI are blocked even with IOPL level 3 */
  148. expect_gp_sti(test_cli());
  149. expect_ok_outb(0x80);
  150. /* Establish an I/O bitmap to test the restore */
  151. if (ioperm(0x80, 1, 1) != 0)
  152. err(1, "ioperm(0x80, 1, 1) failed\n");
  153. /* Restore our original state prior to starting the fork test. */
  154. if (iopl(0) != 0)
  155. err(1, "iopl(0)");
  156. /*
  157. * Verify that IOPL emulation is disabled and the I/O bitmap still
  158. * works.
  159. */
  160. expect_ok_outb(0x80);
  161. expect_gp_outb(0xed);
  162. /* Drop the I/O bitmap */
  163. if (ioperm(0x80, 1, 0) != 0)
  164. err(1, "ioperm(0x80, 1, 0) failed\n");
  165. pid_t child = fork();
  166. if (child == -1)
  167. err(1, "fork");
  168. if (child == 0) {
  169. printf("\tchild: set IOPL to 3\n");
  170. if (iopl(3) != 0)
  171. err(1, "iopl");
  172. printf("[RUN]\tchild: write to 0x80\n");
  173. asm volatile ("outb %%al, $0x80" : : "a" (0));
  174. return 0;
  175. } else {
  176. int status;
  177. if (waitpid(child, &status, 0) != child ||
  178. !WIFEXITED(status)) {
  179. printf("[FAIL]\tChild died\n");
  180. nerrs++;
  181. } else if (WEXITSTATUS(status) != 0) {
  182. printf("[FAIL]\tChild failed\n");
  183. nerrs++;
  184. } else {
  185. printf("[OK]\tChild succeeded\n");
  186. }
  187. }
  188. printf("[RUN]\tparent: write to 0x80 (should fail)\n");
  189. expect_gp_outb(0x80);
  190. expect_gp_sti(test_cli());
  191. /* Test the capability checks. */
  192. printf("\tiopl(3)\n");
  193. if (iopl(3) != 0)
  194. err(1, "iopl(3)");
  195. printf("\tDrop privileges\n");
  196. if (setresuid(1, 1, 1) != 0) {
  197. printf("[WARN]\tDropping privileges failed\n");
  198. goto done;
  199. }
  200. printf("[RUN]\tiopl(3) unprivileged but with IOPL==3\n");
  201. if (iopl(3) != 0) {
  202. printf("[FAIL]\tiopl(3) should work if iopl is already 3 even if unprivileged\n");
  203. nerrs++;
  204. }
  205. printf("[RUN]\tiopl(0) unprivileged\n");
  206. if (iopl(0) != 0) {
  207. printf("[FAIL]\tiopl(0) should work if iopl is already 3 even if unprivileged\n");
  208. nerrs++;
  209. }
  210. printf("[RUN]\tiopl(3) unprivileged\n");
  211. if (iopl(3) == 0) {
  212. printf("[FAIL]\tiopl(3) should fail if when unprivileged if iopl==0\n");
  213. nerrs++;
  214. } else {
  215. printf("[OK]\tFailed as expected\n");
  216. }
  217. done:
  218. return nerrs ? 1 : 0;
  219. }