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