fuzz-syscalls.cpp 5.6 KB

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