fuzz-syscalls.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /*
  2. * Copyright (c) 2021, Ben Wiederhake <BenWiederhake.GitHub@gmx.de>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Format.h>
  7. #include <AK/Random.h>
  8. #include <AK/String.h>
  9. #include <AK/StringBuilder.h>
  10. #include <AK/Vector.h>
  11. #include <Kernel/API/SyscallString.h>
  12. #include <errno.h>
  13. #include <signal.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <sys/mman.h>
  18. #include <syscall.h>
  19. static bool is_deadly_syscall(int fn)
  20. {
  21. return fn == SC_exit || fn == SC_fork || fn == SC_sigreturn || fn == SC_exit_thread;
  22. }
  23. static bool is_unfuzzable_syscall(int fn)
  24. {
  25. return fn == SC_dump_backtrace || fn == SC_munmap || fn == SC_kill || fn == SC_killpg;
  26. }
  27. static bool is_nosys_syscall(int fn)
  28. {
  29. return fn == SC_futex || fn == SC_emuctl;
  30. }
  31. static bool is_bad_idea(int fn, size_t const* direct_sc_args, size_t const* fake_sc_params, char const* some_string)
  32. {
  33. switch (fn) {
  34. case SC_mprotect:
  35. // This would mess with future tests or crash the fuzzer.
  36. return direct_sc_args[0] == (size_t)fake_sc_params || direct_sc_args[0] == (size_t)some_string;
  37. case SC_read:
  38. case SC_readv:
  39. // FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
  40. return direct_sc_args[0] == 1;
  41. case SC_write:
  42. case SC_writev:
  43. // FIXME: Known bug: https://github.com/SerenityOS/serenity/issues/5328
  44. return direct_sc_args[0] == 0;
  45. case SC_pledge:
  46. // Equivalent to pledge(nullptr, _), which would kill the fuzzer.
  47. return direct_sc_args[0] == (size_t)fake_sc_params && fake_sc_params[1] == 0;
  48. default:
  49. return false;
  50. }
  51. }
  52. static void do_systematic_tests()
  53. {
  54. int rc;
  55. for (int i = 0; i < Syscall::Function::__Count; ++i) {
  56. dbgln("Testing syscall #{} ({})", i, Syscall::to_string((Syscall::Function)i));
  57. if (is_deadly_syscall(i)) {
  58. dbgln("(skipping deadly syscall)");
  59. continue;
  60. }
  61. // This is pure torture
  62. rc = syscall(Syscall::Function(i), 0xc0000001, 0xc0000002, 0xc0000003);
  63. VERIFY(rc != -ENOSYS || is_nosys_syscall(i));
  64. }
  65. // Finally, test invalid syscalls:
  66. dbgln("Testing syscall #{} (n+1)", (int)Syscall::Function::__Count);
  67. rc = syscall(Syscall::Function::__Count, 0xc0000001, 0xc0000002, 0xc0000003);
  68. VERIFY(rc == -ENOSYS);
  69. dbgln("Testing syscall #-1");
  70. rc = syscall(Syscall::Function(-1), 0xc0000001, 0xc0000002, 0xc0000003);
  71. VERIFY(rc == -ENOSYS);
  72. }
  73. static void randomize_from(size_t* buffer, size_t len, Vector<size_t> const& values)
  74. {
  75. for (size_t i = 0; i < len; ++i) {
  76. buffer[i] = values[get_random_uniform(values.size())];
  77. }
  78. }
  79. // The largest SC_*_params struct is SC_mmap_params with 9 size_ts (36 bytes on x86, 72 on x86_64).
  80. static constexpr size_t fake_params_count = sizeof(Syscall::SC_mmap_params) / sizeof(size_t);
  81. static void do_weird_call(size_t attempt, int syscall_fn, size_t arg1, size_t arg2, size_t arg3, size_t* fake_params)
  82. {
  83. // Report to dbg what we're about to do, in case it's interesting:
  84. StringBuilder builder;
  85. builder.appendff("#{}: Calling {}({:p}, {:p}, {:p}) with {:p} containing [",
  86. attempt, Syscall::to_string((Syscall::Function)syscall_fn), arg1, arg2, arg3, fake_params);
  87. for (size_t i = 0; i < fake_params_count; ++i) {
  88. if (i != 0)
  89. builder.append(", "sv);
  90. builder.appendff("{:p}", fake_params[i]);
  91. }
  92. builder.append(']');
  93. dbgln("{}", builder.build());
  94. // Actually do the syscall ('fake_params' is passed indirectly, if any of arg1, arg2, or arg3 point to it.
  95. int rc = syscall(Syscall::Function(syscall_fn), arg1, arg2, arg3);
  96. VERIFY(rc != -ENOSYS || is_nosys_syscall(syscall_fn));
  97. }
  98. static void do_random_tests()
  99. {
  100. // Make it less likely to kill ourselves due to sys$alarm(1):
  101. {
  102. struct sigaction act_ignore = { { SIG_IGN }, 0, 0 };
  103. int rc = sigaction(SIGALRM, &act_ignore, nullptr);
  104. VERIFY(rc == 0);
  105. }
  106. // Note that we will also make lots of syscalls for randomness and debugging.
  107. const size_t fuzz_syscall_count = 10000;
  108. size_t direct_sc_args[3] = { 0 };
  109. // Isolate to a separate region to make corruption less likely, because we will write to it:
  110. auto* fake_sc_params = reinterpret_cast<size_t*>(mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_RANDOMIZED, 0, 0));
  111. char const* some_string = "Hello, world!";
  112. Vector<size_t> interesting_values = {
  113. 0,
  114. 1,
  115. reinterpret_cast<size_t>(some_string),
  116. strlen(some_string),
  117. reinterpret_cast<size_t>(fake_sc_params),
  118. 0xc0000000,
  119. 0xc0000000 - PAGE_SIZE,
  120. 0xffffffff,
  121. };
  122. dbgln("Doing a few random syscalls with:");
  123. for (auto const& interesting_value : interesting_values) {
  124. dbgln(" {0} ({0:p})", interesting_value);
  125. }
  126. for (size_t i = 0; i < fuzz_syscall_count; ++i) {
  127. // Construct a nice syscall:
  128. int syscall_fn = get_random_uniform(Syscall::Function::__Count);
  129. randomize_from(direct_sc_args, array_size(direct_sc_args), interesting_values);
  130. randomize_from(fake_sc_params, fake_params_count, interesting_values);
  131. if (is_deadly_syscall(syscall_fn)
  132. || is_unfuzzable_syscall(syscall_fn)
  133. || is_bad_idea(syscall_fn, direct_sc_args, fake_sc_params, some_string)) {
  134. // Retry, and don't count towards syscall limit.
  135. --i;
  136. continue;
  137. }
  138. do_weird_call(i, syscall_fn, direct_sc_args[0], direct_sc_args[1], direct_sc_args[2], fake_sc_params);
  139. }
  140. }
  141. int main()
  142. {
  143. do_systematic_tests();
  144. do_random_tests();
  145. // If the Kernel survived, pass.
  146. printf("PASS\n");
  147. return 0;
  148. }