Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "test/fuzzer/wasm-fuzzer-common.h"
6 :
7 : #include "include/v8.h"
8 : #include "src/isolate.h"
9 : #include "src/objects-inl.h"
10 : #include "src/wasm/module-compiler.h"
11 : #include "src/wasm/wasm-api.h"
12 : #include "src/wasm/wasm-module-builder.h"
13 : #include "src/wasm/wasm-module.h"
14 : #include "src/zone/accounting-allocator.h"
15 : #include "src/zone/zone.h"
16 : #include "test/common/wasm/wasm-module-runner.h"
17 : #include "test/fuzzer/fuzzer-support.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 : namespace wasm {
22 : namespace fuzzer {
23 :
24 : static constexpr uint32_t kWasmCodeFuzzerHashSeed = 83;
25 :
26 : static constexpr const char* kNameString = "name";
27 : static constexpr size_t kNameStringLength = 4;
28 :
29 7 : int FuzzWasmSection(SectionCode section, const uint8_t* data, size_t size) {
30 7 : v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
31 7 : v8::Isolate* isolate = support->GetIsolate();
32 : i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
33 :
34 : // Clear any pending exceptions from a prior run.
35 7 : if (i_isolate->has_pending_exception()) {
36 : i_isolate->clear_pending_exception();
37 : }
38 :
39 : v8::Isolate::Scope isolate_scope(isolate);
40 14 : v8::HandleScope handle_scope(isolate);
41 7 : v8::Context::Scope context_scope(support->GetContext());
42 14 : v8::TryCatch try_catch(isolate);
43 :
44 14 : AccountingAllocator allocator;
45 14 : Zone zone(&allocator, ZONE_NAME);
46 :
47 : ZoneBuffer buffer(&zone);
48 : buffer.write_u32(kWasmMagic);
49 : buffer.write_u32(kWasmVersion);
50 7 : if (section == kNameSectionCode) {
51 : buffer.write_u8(kUnknownSectionCode);
52 1 : buffer.write_size(size + kNameStringLength + 1);
53 : buffer.write_u8(kNameStringLength);
54 : buffer.write(reinterpret_cast<const uint8_t*>(kNameString),
55 1 : kNameStringLength);
56 1 : buffer.write(data, size);
57 : } else {
58 6 : buffer.write_u8(section);
59 6 : buffer.write_size(size);
60 6 : buffer.write(data, size);
61 : }
62 :
63 7 : ErrorThrower thrower(i_isolate, "decoder");
64 :
65 : std::unique_ptr<const WasmModule> module(testing::DecodeWasmModuleForTesting(
66 14 : i_isolate, &thrower, buffer.begin(), buffer.end(), kWasmOrigin));
67 :
68 7 : return 0;
69 : }
70 :
71 2 : void InterpretAndExecuteModule(i::Isolate* isolate,
72 : Handle<WasmModuleObject> module_object) {
73 : ScheduledErrorThrower thrower(isolate, "WebAssembly Instantiation");
74 : // Try to instantiate and interpret the module_object.
75 : MaybeHandle<WasmInstanceObject> maybe_instance =
76 : SyncInstantiate(isolate, &thrower, module_object,
77 : Handle<JSReceiver>::null(), // imports
78 2 : MaybeHandle<JSArrayBuffer>()); // memory
79 : Handle<WasmInstanceObject> instance;
80 4 : if (!maybe_instance.ToHandle(&instance)) return;
81 1 : if (!testing::InterpretWasmModuleForTesting(isolate, instance, "main", 0,
82 1 : nullptr)) {
83 : return;
84 : }
85 :
86 : // Instantiate and execute the module_object.
87 : maybe_instance = SyncInstantiate(isolate, &thrower, module_object,
88 : Handle<JSReceiver>::null(), // imports
89 0 : MaybeHandle<JSArrayBuffer>()); // memory
90 0 : if (!maybe_instance.ToHandle(&instance)) return;
91 :
92 0 : testing::RunWasmModuleForTesting(isolate, instance, 0, nullptr);
93 : }
94 :
95 3 : int WasmExecutionFuzzer::FuzzWasmModule(
96 : const uint8_t* data, size_t size) {
97 : // Save the flag so that we can change it and restore it later.
98 3 : bool generate_test = FLAG_wasm_code_fuzzer_gen_test;
99 3 : if (generate_test) {
100 0 : OFStream os(stdout);
101 :
102 0 : os << "// Copyright 2017 the V8 project authors. All rights reserved."
103 : << std::endl;
104 : os << "// Use of this source code is governed by a BSD-style license that "
105 0 : "can be"
106 : << std::endl;
107 0 : os << "// found in the LICENSE file." << std::endl;
108 : os << std::endl;
109 0 : os << "load(\"test/mjsunit/wasm/wasm-constants.js\");" << std::endl;
110 0 : os << "load(\"test/mjsunit/wasm/wasm-module-builder.js\");" << std::endl;
111 : os << std::endl;
112 0 : os << "(function() {" << std::endl;
113 0 : os << " var builder = new WasmModuleBuilder();" << std::endl;
114 0 : os << " builder.addMemory(16, 32, false);" << std::endl;
115 0 : os << " builder.addFunction(\"test\", kSig_i_iii)" << std::endl;
116 0 : os << " .addBodyWithEnd([" << std::endl;
117 : }
118 3 : v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
119 3 : v8::Isolate* isolate = support->GetIsolate();
120 : i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
121 :
122 : // Clear any pending exceptions from a prior run.
123 3 : if (i_isolate->has_pending_exception()) {
124 : i_isolate->clear_pending_exception();
125 : }
126 :
127 : v8::Isolate::Scope isolate_scope(isolate);
128 6 : v8::HandleScope handle_scope(isolate);
129 3 : v8::Context::Scope context_scope(support->GetContext());
130 6 : v8::TryCatch try_catch(isolate);
131 : HandleScope scope(i_isolate);
132 :
133 6 : AccountingAllocator allocator;
134 6 : Zone zone(&allocator, ZONE_NAME);
135 :
136 : ZoneBuffer buffer(&zone);
137 3 : int32_t num_args = 0;
138 3 : std::unique_ptr<WasmValue[]> interpreter_args;
139 3 : std::unique_ptr<Handle<Object>[]> compiler_args;
140 3 : if (!GenerateModule(i_isolate, &zone, data, size, buffer, num_args,
141 3 : interpreter_args, compiler_args)) {
142 : return 0;
143 : }
144 :
145 2 : testing::SetupIsolateForWasmModule(i_isolate);
146 :
147 2 : ErrorThrower interpreter_thrower(i_isolate, "Interpreter");
148 2 : ModuleWireBytes wire_bytes(buffer.begin(), buffer.end());
149 :
150 : MaybeHandle<WasmModuleObject> compiled_module =
151 2 : SyncCompile(i_isolate, &interpreter_thrower, wire_bytes);
152 : // Clear the flag so that the WebAssembly code is not printed twice.
153 2 : FLAG_wasm_code_fuzzer_gen_test = false;
154 2 : bool compiles = !compiled_module.is_null();
155 :
156 2 : if (generate_test) {
157 0 : OFStream os(stdout);
158 0 : os << " ])" << std::endl
159 0 : << " .exportFunc();" << std::endl;
160 0 : if (compiles) {
161 0 : os << " var module = builder.instantiate();" << std::endl
162 0 : << " module.exports.test(1, 2, 3);" << std::endl;
163 : } else {
164 0 : OFStream os(stdout);
165 0 : os << " assertThrows(function() { builder.instantiate(); });"
166 0 : << std::endl;
167 : }
168 0 : os << "})();" << std::endl;
169 : }
170 :
171 2 : bool validates = SyncValidate(i_isolate, wire_bytes);
172 :
173 2 : if (compiles != validates) {
174 : uint32_t hash = StringHasher::HashSequentialString(
175 0 : data, static_cast<int>(size), kWasmCodeFuzzerHashSeed);
176 : V8_Fatal(__FILE__, __LINE__,
177 : "compiles != validates (%d vs %d); WasmCodeFuzzerHash=%x",
178 0 : compiles, validates, hash);
179 : }
180 :
181 2 : if (!compiles) return 0;
182 :
183 : int32_t result_interpreted;
184 1 : bool possible_nondeterminism = false;
185 : {
186 : MaybeHandle<WasmInstanceObject> interpreter_instance = SyncInstantiate(
187 : i_isolate, &interpreter_thrower, compiled_module.ToHandleChecked(),
188 1 : MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>());
189 :
190 1 : if (interpreter_thrower.error()) {
191 0 : return 0;
192 : }
193 : result_interpreted = testing::InterpretWasmModule(
194 : i_isolate, interpreter_instance.ToHandleChecked(), &interpreter_thrower,
195 1 : 0, interpreter_args.get(), &possible_nondeterminism);
196 : }
197 :
198 : // Do not execute the generated code if the interpreter did not finished after
199 : // a bounded number of steps.
200 1 : if (interpreter_thrower.error()) {
201 : return 0;
202 : }
203 :
204 : int32_t result_compiled;
205 : {
206 : ErrorThrower compiler_thrower(i_isolate, "Compiler");
207 : MaybeHandle<WasmInstanceObject> compiled_instance = SyncInstantiate(
208 : i_isolate, &compiler_thrower, compiled_module.ToHandleChecked(),
209 1 : MaybeHandle<JSReceiver>(), MaybeHandle<JSArrayBuffer>());
210 :
211 : DCHECK(!compiler_thrower.error());
212 : result_compiled = testing::CallWasmFunctionForTesting(
213 : i_isolate, compiled_instance.ToHandleChecked(), &compiler_thrower,
214 2 : "main", num_args, compiler_args.get());
215 : }
216 :
217 : // The WebAssembly spec allows the sign bit of NaN to be non-deterministic.
218 : // This sign bit may cause result_interpreted to be different than
219 : // result_compiled. Therefore we do not check the equality of the results
220 : // if the execution may have produced a NaN at some point.
221 1 : if (possible_nondeterminism) return 0;
222 :
223 1 : if (result_interpreted == bit_cast<int32_t>(0xdeadbeef)) {
224 0 : CHECK(i_isolate->has_pending_exception());
225 : i_isolate->clear_pending_exception();
226 : } else {
227 1 : CHECK(!i_isolate->has_pending_exception());
228 1 : if (result_interpreted != result_compiled) {
229 : V8_Fatal(__FILE__, __LINE__, "WasmCodeFuzzerHash=%x",
230 : StringHasher::HashSequentialString(data, static_cast<int>(size),
231 0 : kWasmCodeFuzzerHashSeed));
232 : }
233 : }
234 : return 0;
235 : }
236 :
237 : } // namespace fuzzer
238 : } // namespace wasm
239 : } // namespace internal
240 : } // namespace v8
|