FuzzilliJs.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Format.h>
  7. #include <AK/Function.h>
  8. #include <AK/StringView.h>
  9. #include <LibJS/Bytecode/Interpreter.h>
  10. #include <LibJS/Forward.h>
  11. #include <LibJS/Lexer.h>
  12. #include <LibJS/Parser.h>
  13. #include <LibJS/Runtime/GlobalObject.h>
  14. #include <errno.h>
  15. #include <stddef.h>
  16. #include <stdint.h>
  17. #include <sys/mman.h>
  18. #include <fcntl.h>
  19. #include <string.h>
  20. #include <sys/stat.h>
  21. #include <sys/types.h>
  22. // These are hooks into sancov's internals, their declaration isn't available in public headers.
  23. // See compiler-rt/lib/sanitizer_common/sanitizer_interface_internal.h
  24. extern "C" {
  25. void __sanitizer_cov_trace_pc_guard_init(uint32_t*, uint32_t*);
  26. void __sanitizer_cov_trace_pc_guard(uint32_t*);
  27. }
  28. //
  29. // BEGIN FUZZING CODE
  30. //
  31. #define REPRL_CRFD 100
  32. #define REPRL_CWFD 101
  33. #define REPRL_DRFD 102
  34. #define REPRL_DWFD 103
  35. #define REPRL_MAX_DATA_SIZE (16 * 1024 * 1024)
  36. #define SHM_SIZE 0x100000
  37. #define MAX_EDGES ((SHM_SIZE - 4) * 8)
  38. #define CHECK(cond) \
  39. if (!(cond)) { \
  40. fprintf(stderr, "\"" #cond "\" failed\n"); \
  41. _exit(-1); \
  42. }
  43. struct shmem_data {
  44. uint32_t num_edges;
  45. unsigned char edges[];
  46. };
  47. struct shmem_data* __shmem;
  48. uint32_t *__edges_start, *__edges_stop;
  49. static void __sanitizer_cov_reset_edgeguards()
  50. {
  51. uint64_t N = 0;
  52. for (uint32_t* x = __edges_start; x < __edges_stop && N < MAX_EDGES; x++)
  53. *x = ++N;
  54. }
  55. extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start, uint32_t* stop)
  56. {
  57. // Avoid duplicate initialization
  58. if (start == stop || *start)
  59. return;
  60. if (__edges_start != NULL || __edges_stop != NULL) {
  61. fprintf(stderr, "Coverage instrumentation is only supported for a single module\n");
  62. _exit(-1);
  63. }
  64. __edges_start = start;
  65. __edges_stop = stop;
  66. // Map the shared memory region
  67. char const* shm_key = getenv("SHM_ID");
  68. if (!shm_key) {
  69. puts("[COV] no shared memory bitmap available, skipping");
  70. __shmem = (struct shmem_data*)malloc(SHM_SIZE);
  71. } else {
  72. int fd = shm_open(shm_key, O_RDWR, S_IREAD | S_IWRITE);
  73. if (fd <= -1) {
  74. fprintf(stderr, "Failed to open shared memory region: %s\n", strerror(errno));
  75. _exit(-1);
  76. }
  77. __shmem = (struct shmem_data*)mmap(0, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  78. if (__shmem == MAP_FAILED) {
  79. fprintf(stderr, "Failed to mmap shared memory region\n");
  80. _exit(-1);
  81. }
  82. }
  83. __sanitizer_cov_reset_edgeguards();
  84. __shmem->num_edges = stop - start;
  85. printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n", shm_key, __shmem->num_edges);
  86. }
  87. extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard)
  88. {
  89. // There's a small race condition here: if this function executes in two threads for the same
  90. // edge at the same time, the first thread might disable the edge (by setting the guard to zero)
  91. // before the second thread fetches the guard value (and thus the index). However, our
  92. // instrumentation ignores the first edge (see libcoverage.c) and so the race is unproblematic.
  93. uint32_t index = *guard;
  94. // If this function is called before coverage instrumentation is properly initialized we want to return early.
  95. if (!index)
  96. return;
  97. __shmem->edges[index / 8] |= 1 << (index % 8);
  98. *guard = 0;
  99. }
  100. //
  101. // END FUZZING CODE
  102. //
  103. class TestRunnerGlobalObject final : public JS::GlobalObject {
  104. JS_OBJECT(TestRunnerGlobalObject, JS::GlobalObject);
  105. public:
  106. TestRunnerGlobalObject(JS::Realm&);
  107. virtual void initialize(JS::Realm&) override;
  108. virtual ~TestRunnerGlobalObject() override;
  109. private:
  110. JS_DECLARE_NATIVE_FUNCTION(fuzzilli);
  111. };
  112. TestRunnerGlobalObject::TestRunnerGlobalObject(JS::Realm& realm)
  113. : GlobalObject(realm)
  114. {
  115. }
  116. TestRunnerGlobalObject::~TestRunnerGlobalObject()
  117. {
  118. }
  119. JS_DEFINE_NATIVE_FUNCTION(TestRunnerGlobalObject::fuzzilli)
  120. {
  121. if (!vm.argument_count())
  122. return JS::js_undefined();
  123. auto operation = TRY(vm.argument(0).to_string(vm));
  124. if (operation == "FUZZILLI_CRASH") {
  125. auto type = TRY(vm.argument(1).to_i32(vm));
  126. switch (type) {
  127. case 0:
  128. *((int*)0x41414141) = 0x1337;
  129. break;
  130. default:
  131. VERIFY_NOT_REACHED();
  132. break;
  133. }
  134. } else if (operation == "FUZZILLI_PRINT") {
  135. static FILE* fzliout = fdopen(REPRL_DWFD, "w");
  136. if (!fzliout) {
  137. dbgln("Fuzzer output not available");
  138. fzliout = stdout;
  139. }
  140. auto string = TRY(vm.argument(1).to_string(vm));
  141. outln(fzliout, "{}", string);
  142. fflush(fzliout);
  143. }
  144. return JS::js_undefined();
  145. }
  146. void TestRunnerGlobalObject::initialize(JS::Realm& realm)
  147. {
  148. Base::initialize(realm);
  149. define_direct_property("global", this, JS::Attribute::Enumerable);
  150. define_native_function(realm, "fuzzilli", fuzzilli, 2, JS::default_attributes);
  151. }
  152. int main(int, char**)
  153. {
  154. char* reprl_input = nullptr;
  155. char helo[] = "HELO";
  156. if (write(REPRL_CWFD, helo, 4) != 4 || read(REPRL_CRFD, helo, 4) != 4) {
  157. VERIFY_NOT_REACHED();
  158. }
  159. VERIFY(memcmp(helo, "HELO", 4) == 0);
  160. reprl_input = (char*)mmap(0, REPRL_MAX_DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, REPRL_DRFD, 0);
  161. VERIFY(reprl_input != MAP_FAILED);
  162. auto vm = MUST(JS::VM::create());
  163. auto root_execution_context = JS::create_simple_execution_context<TestRunnerGlobalObject>(*vm);
  164. auto& realm = *root_execution_context->realm;
  165. while (true) {
  166. unsigned action;
  167. VERIFY(read(REPRL_CRFD, &action, 4) == 4);
  168. VERIFY(action == 'cexe');
  169. size_t script_size;
  170. VERIFY(read(REPRL_CRFD, &script_size, 8) == 8);
  171. VERIFY(script_size < REPRL_MAX_DATA_SIZE);
  172. ByteBuffer data_buffer;
  173. data_buffer.resize(script_size);
  174. VERIFY(data_buffer.size() >= script_size);
  175. memcpy(data_buffer.data(), reprl_input, script_size);
  176. int result = 0;
  177. auto js = StringView(static_cast<unsigned char const*>(data_buffer.data()), script_size);
  178. // FIXME: https://github.com/SerenityOS/serenity/issues/17899
  179. if (!Utf8View(js).validate()) {
  180. result = 1;
  181. } else {
  182. auto parse_result = JS::Script::parse(js, realm);
  183. if (parse_result.is_error()) {
  184. result = 1;
  185. } else {
  186. auto completion = vm->bytecode_interpreter().run(parse_result.value());
  187. if (completion.is_error()) {
  188. result = 1;
  189. }
  190. }
  191. }
  192. fflush(stdout);
  193. fflush(stderr);
  194. int status = (result & 0xff) << 8;
  195. VERIFY(write(REPRL_CWFD, &status, 4) == 4);
  196. __sanitizer_cov_reset_edgeguards();
  197. }
  198. return 0;
  199. }