1 // Copyright 2018 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 <cstddef>
6 #include <cstdint>
7
8 #include "src/codegen/machine-type.h"
9 #include "src/codegen/optimized-compilation-info.h"
10 #include "src/compiler/backend/instruction-selector.h"
11 #include "src/compiler/graph.h"
12 #include "src/compiler/linkage.h"
13 #include "src/compiler/node.h"
14 #include "src/compiler/operator.h"
15 #include "src/compiler/pipeline.h"
16 #include "src/compiler/raw-machine-assembler.h"
17 #include "src/compiler/wasm-compiler.h"
18 #include "src/execution/simulator.h"
19 #include "src/objects/objects-inl.h"
20 #include "src/objects/objects.h"
21 #include "src/wasm/wasm-engine.h"
22 #include "src/wasm/wasm-features.h"
23 #include "src/wasm/wasm-limits.h"
24 #include "src/wasm/wasm-objects-inl.h"
25 #include "src/wasm/wasm-objects.h"
26 #include "src/wasm/wasm-opcodes.h"
27 #include "src/zone/accounting-allocator.h"
28 #include "src/zone/zone.h"
29 #include "test/fuzzer/fuzzer-support.h"
30
31 namespace v8 {
32 namespace internal {
33 namespace compiler {
34 namespace fuzzer {
35
36 constexpr MachineType kTypes[] = {
37 // The first entry is just a placeholder, because '0' is a separator.
38 MachineType(),
39 #if !V8_TARGET_ARCH_32_BIT
40 MachineType::Int64(),
41 #endif
42 MachineType::Int32(), MachineType::Float32(), MachineType::Float64()};
43
44 static constexpr int kNumTypes = arraysize(kTypes);
45
46 class InputProvider {
47 public:
InputProvider(const uint8_t * data,size_t size)48 InputProvider(const uint8_t* data, size_t size)
49 : current_(data), end_(data + size) {}
50
NumNonZeroBytes(size_t offset,int limit)51 size_t NumNonZeroBytes(size_t offset, int limit) {
52 DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
53 DCHECK_GE(current_ + offset, current_);
54 const uint8_t* p;
55 for (p = current_ + offset; p < end_; ++p) {
56 if (*p % limit == 0) break;
57 }
58 return p - current_ - offset;
59 }
60
NextInt8(int limit)61 int NextInt8(int limit) {
62 DCHECK_LE(limit, std::numeric_limits<uint8_t>::max());
63 if (current_ == end_) return 0;
64 uint8_t result = *current_;
65 current_++;
66 return static_cast<int>(result) % limit;
67 }
68
NextInt32(int limit)69 int NextInt32(int limit) {
70 if (current_ + sizeof(uint32_t) > end_) return 0;
71 int result =
72 base::ReadLittleEndianValue<int>(reinterpret_cast<Address>(current_));
73 current_ += sizeof(uint32_t);
74 return result % limit;
75 }
76
77 private:
78 const uint8_t* current_;
79 const uint8_t* end_;
80 };
81
RandomType(InputProvider * input)82 MachineType RandomType(InputProvider* input) {
83 return kTypes[input->NextInt8(kNumTypes)];
84 }
85
index(MachineType type)86 int index(MachineType type) { return static_cast<int>(type.representation()); }
87
Constant(RawMachineAssembler * m,MachineType type,int value)88 Node* Constant(RawMachineAssembler* m, MachineType type, int value) {
89 switch (type.representation()) {
90 case MachineRepresentation::kWord32:
91 return m->Int32Constant(static_cast<int32_t>(value));
92 case MachineRepresentation::kWord64:
93 return m->Int64Constant(static_cast<int64_t>(value));
94 case MachineRepresentation::kFloat32:
95 return m->Float32Constant(static_cast<float>(value));
96 case MachineRepresentation::kFloat64:
97 return m->Float64Constant(static_cast<double>(value));
98 default:
99 UNREACHABLE();
100 }
101 }
102
ToInt32(RawMachineAssembler * m,MachineType type,Node * a)103 Node* ToInt32(RawMachineAssembler* m, MachineType type, Node* a) {
104 switch (type.representation()) {
105 case MachineRepresentation::kWord32:
106 return a;
107 case MachineRepresentation::kWord64:
108 return m->TruncateInt64ToInt32(a);
109 case MachineRepresentation::kFloat32:
110 return m->TruncateFloat32ToInt32(a, TruncateKind::kArchitectureDefault);
111 case MachineRepresentation::kFloat64:
112 return m->RoundFloat64ToInt32(a);
113 default:
114 UNREACHABLE();
115 }
116 }
117
CreateRandomCallDescriptor(Zone * zone,size_t return_count,size_t param_count,InputProvider * input)118 CallDescriptor* CreateRandomCallDescriptor(Zone* zone, size_t return_count,
119 size_t param_count,
120 InputProvider* input) {
121 wasm::FunctionSig::Builder builder(zone, return_count, param_count);
122 for (size_t i = 0; i < param_count; i++) {
123 MachineType type = RandomType(input);
124 builder.AddParam(wasm::ValueType::For(type));
125 }
126 // Read the end byte of the parameters.
127 input->NextInt8(1);
128
129 for (size_t i = 0; i < return_count; i++) {
130 MachineType type = RandomType(input);
131 builder.AddReturn(wasm::ValueType::For(type));
132 }
133
134 return compiler::GetWasmCallDescriptor(zone, builder.Build());
135 }
136
AllocateNativeModule(i::Isolate * isolate,size_t code_size)137 std::shared_ptr<wasm::NativeModule> AllocateNativeModule(i::Isolate* isolate,
138 size_t code_size) {
139 std::shared_ptr<wasm::WasmModule> module(new wasm::WasmModule);
140 module->num_declared_functions = 1;
141
142 // We have to add the code object to a NativeModule, because the
143 // WasmCallDescriptor assumes that code is on the native heap and not
144 // within a code object.
145 auto native_module = wasm::GetWasmEngine()->NewNativeModule(
146 isolate, i::wasm::WasmFeatures::All(), std::move(module), code_size);
147 native_module->SetWireBytes({});
148 return native_module;
149 }
150
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)151 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
152 v8_fuzzer::FuzzerSupport* support = v8_fuzzer::FuzzerSupport::Get();
153 v8::Isolate* isolate = support->GetIsolate();
154 i::Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
155 v8::Isolate::Scope isolate_scope(isolate);
156 v8::HandleScope handle_scope(isolate);
157 v8::Context::Scope context_scope(support->GetContext());
158 v8::TryCatch try_catch(isolate);
159 v8::internal::AccountingAllocator allocator;
160 Zone zone(&allocator, ZONE_NAME);
161
162 InputProvider input(data, size);
163 // Create randomized descriptor.
164 size_t param_count = input.NumNonZeroBytes(0, kNumTypes);
165 if (param_count > Code::kMaxArguments) return 0;
166
167 size_t return_count = input.NumNonZeroBytes(param_count + 1, kNumTypes);
168 if (return_count > wasm::kV8MaxWasmFunctionReturns) return 0;
169
170 CallDescriptor* desc =
171 CreateRandomCallDescriptor(&zone, return_count, param_count, &input);
172
173 if (FLAG_wasm_fuzzer_gen_test) {
174 // Print some debugging output which describes the produced signature.
175 printf("[");
176 for (size_t j = 0; j < param_count; ++j) {
177 // Parameter 0 is the WasmContext.
178 printf(" %s", MachineReprToString(
179 desc->GetParameterType(j + 1).representation()));
180 }
181 printf(" ] -> [");
182 for (size_t j = 0; j < desc->ReturnCount(); ++j) {
183 printf(" %s",
184 MachineReprToString(desc->GetReturnType(j).representation()));
185 }
186 printf(" ]\n\n");
187 }
188
189 // Count parameters of each type.
190 constexpr size_t kNumMachineRepresentations =
191 static_cast<size_t>(MachineRepresentation::kLastRepresentation) + 1;
192
193 // Trivial hash table for the number of occurrences of parameter types. The
194 // MachineRepresentation of the parameter types is used as hash code.
195 int counts[kNumMachineRepresentations] = {0};
196 for (size_t i = 0; i < param_count; ++i) {
197 // Parameter 0 is the WasmContext.
198 ++counts[index(desc->GetParameterType(i + 1))];
199 }
200
201 // Generate random inputs.
202 std::unique_ptr<int[]> inputs(new int[param_count]);
203 std::unique_ptr<int[]> outputs(new int[desc->ReturnCount()]);
204 for (size_t i = 0; i < param_count; ++i) {
205 inputs[i] = input.NextInt32(10000);
206 }
207
208 RawMachineAssembler callee(
209 i_isolate, zone.New<Graph>(&zone), desc,
210 MachineType::PointerRepresentation(),
211 InstructionSelector::SupportedMachineOperatorFlags());
212
213 // Generate callee, returning random picks of its parameters.
214 std::unique_ptr<Node* []> params(new Node*[desc->ParameterCount() + 2]);
215 // The first input of a return is the number of stack slots that should be
216 // popped before returning.
217 std::unique_ptr<Node* []> returns(new Node*[desc->ReturnCount() + 1]);
218 for (size_t i = 0; i < param_count; ++i) {
219 // Parameter(0) is the WasmContext.
220 params[i] = callee.Parameter(i + 1);
221 }
222
223 for (size_t i = 0; i < desc->ReturnCount(); ++i) {
224 MachineType type = desc->GetReturnType(i);
225 // Find a random same-type parameter to return. Use a constant if none.
226 if (counts[index(type)] == 0) {
227 returns[i] = Constant(&callee, type, 42);
228 outputs[i] = 42;
229 } else {
230 int n = input.NextInt32(counts[index(type)]);
231 int k = 0;
232 while (desc->GetParameterType(k + 1) != desc->GetReturnType(i) ||
233 --n > 0) {
234 ++k;
235 }
236 returns[i] = params[k];
237 outputs[i] = inputs[k];
238 }
239 }
240 callee.Return(static_cast<int>(desc->ReturnCount()), returns.get());
241
242 OptimizedCompilationInfo info(base::ArrayVector("testing"), &zone,
243 CodeKind::FOR_TESTING);
244 Handle<Code> code =
245 Pipeline::GenerateCodeForTesting(&info, i_isolate, desc, callee.graph(),
246 AssemblerOptions::Default(i_isolate),
247 callee.ExportForTest())
248 .ToHandleChecked();
249
250 std::shared_ptr<wasm::NativeModule> module =
251 AllocateNativeModule(i_isolate, code->raw_instruction_size());
252 wasm::WasmCodeRefScope wasm_code_ref_scope;
253 byte* code_start = module->AddCodeForTesting(code)->instructions().begin();
254 // Generate wrapper.
255 int expect = 0;
256
257 MachineSignature::Builder sig_builder(&zone, 1, 0);
258 sig_builder.AddReturn(MachineType::Int32());
259
260 CallDescriptor* wrapper_desc =
261 Linkage::GetSimplifiedCDescriptor(&zone, sig_builder.Build());
262 RawMachineAssembler caller(
263 i_isolate, zone.New<Graph>(&zone), wrapper_desc,
264 MachineType::PointerRepresentation(),
265 InstructionSelector::SupportedMachineOperatorFlags());
266
267 params[0] = caller.PointerConstant(code_start);
268 // WasmContext dummy.
269 params[1] = caller.PointerConstant(nullptr);
270 for (size_t i = 0; i < param_count; ++i) {
271 params[i + 2] = Constant(&caller, desc->GetParameterType(i + 1), inputs[i]);
272 }
273 Node* call = caller.AddNode(caller.common()->Call(desc),
274 static_cast<int>(param_count + 2), params.get());
275 Node* ret = Constant(&caller, MachineType::Int32(), 0);
276 for (size_t i = 0; i < desc->ReturnCount(); ++i) {
277 // Skip roughly one third of the outputs.
278 if (input.NextInt8(3) == 0) continue;
279 Node* ret_i = (desc->ReturnCount() == 1)
280 ? call
281 : caller.AddNode(caller.common()->Projection(i), call);
282 ret = caller.Int32Add(ret, ToInt32(&caller, desc->GetReturnType(i), ret_i));
283 expect += outputs[i];
284 }
285 caller.Return(ret);
286
287 // Call the wrapper.
288 OptimizedCompilationInfo wrapper_info(base::ArrayVector("wrapper"), &zone,
289 CodeKind::FOR_TESTING);
290 Handle<Code> wrapper_code =
291 Pipeline::GenerateCodeForTesting(
292 &wrapper_info, i_isolate, wrapper_desc, caller.graph(),
293 AssemblerOptions::Default(i_isolate), caller.ExportForTest())
294 .ToHandleChecked();
295
296 auto fn = GeneratedCode<int32_t>::FromCode(*wrapper_code);
297 int result = fn.Call();
298
299 CHECK_EQ(expect, result);
300 return 0;
301 }
302
303 } // namespace fuzzer
304 } // namespace compiler
305 } // namespace internal
306 } // namespace v8
307