| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // Random-walk fuzzer for the Dart VM. |
| 6 // |
| 7 // Start with all the classes and libraries and various interesting values. |
| 8 // Repeatedly choose one as a receiver, construct a message it is likely to |
| 9 // understand, send the message, and add the result. |
| 10 // |
| 11 // Intentionally not run on the build bots because through dart:io it could |
| 12 // trash their setups. |
| 13 |
| 14 library fuzzer; |
| 15 |
| 16 import 'dart:io'; |
| 17 import 'dart:math'; |
| 18 import 'dart:mirrors'; |
| 19 import 'dart:typed_data'; |
| 20 |
| 21 var blacklist = [ |
| 22 'dart.io.exit', |
| 23 'dart.io.exitCode', |
| 24 'dart.io.sleep', |
| 25 'dart.io.Process.killPid', |
| 26 ]; |
| 27 |
| 28 final bool trace = false; |
| 29 |
| 30 void main(List<String> args) { |
| 31 int seed; |
| 32 if (args.length == 1) { |
| 33 seed = int.parse(args[0]); |
| 34 } else { |
| 35 // Dart's built-in random number generator doesn't provide access to the |
| 36 // seed when it is choosen by the implementation. We need to be able to |
| 37 // report this seed to make runs of the fuzzer reproducible, so we create |
| 38 // the seed ourselves. |
| 39 |
| 40 // When running on many machines in parallel, the current time alone |
| 41 // is a poor choice of seed. |
| 42 seed = 0; |
| 43 try { |
| 44 var f = new File("/dev/urandom").openSync(); |
| 45 seed = (seed << 8) | f.readByteSync(); |
| 46 seed = (seed << 8) | f.readByteSync(); |
| 47 seed = (seed << 8) | f.readByteSync(); |
| 48 seed = (seed << 8) | f.readByteSync(); |
| 49 f.close(); |
| 50 } catch(e) { |
| 51 print("Failed to read from /dev/urandom: $e"); |
| 52 } |
| 53 |
| 54 seed ^= new DateTime.now().millisecondsSinceEpoch; |
| 55 seed &= 0xFFFFFFFF; |
| 56 } |
| 57 random = new Random(seed); |
| 58 |
| 59 // Information needed to reproduce this run. |
| 60 print("Dart VM fuzzer"); |
| 61 print("Executable: ${Platform.executable}"); |
| 62 print("Arguments: ${Platform.executableArguments}"); |
| 63 print("Version: ${Platform.version}"); |
| 64 print("Seed: ${seed}"); |
| 65 print("------------------------------------------"); |
| 66 |
| 67 setupInterestingValues(); |
| 68 setupClasses(); |
| 69 |
| 70 // Bound the number of steps in our random walk so that if any issue is found |
| 71 // it can be reproduced without having to wait too long. |
| 72 for (int i = 0; i < 100000; i++) { |
| 73 fuzz(randomElementOf(candidateReceivers)); |
| 74 if (maybe(0.01)) garbageCollect(); |
| 75 } |
| 76 } |
| 77 |
| 78 Random random; |
| 79 |
| 80 bool maybe(probability) => random.nextDouble() < probability; |
| 81 |
| 82 randomElementOf(list) { |
| 83 return list.length == 0 ? null : list[random.nextInt(list.length)]; |
| 84 } |
| 85 |
| 86 class Candidate<T> { |
| 87 Candidate origin; |
| 88 String message; |
| 89 T mirror; |
| 90 trace() { |
| 91 if (origin == null) { |
| 92 print(""); |
| 93 } else { |
| 94 origin.trace(); |
| 95 print(" $message"); |
| 96 } |
| 97 print(mirror); |
| 98 } |
| 99 } |
| 100 |
| 101 List<Candidate<ObjectMirror>> candidateReceivers = |
| 102 new List<Candidate<ObjectMirror>>(); |
| 103 List<Candidate<InstanceMirror>> candidateArguments = |
| 104 new List<Candidate<InstanceMirror>>(); |
| 105 |
| 106 void addInstance(var instance) { |
| 107 addInstanceMirror(reflect(instance)); |
| 108 } |
| 109 |
| 110 void addInstanceMirror(InstanceMirror mirror, |
| 111 [Candidate origin, String message]) { |
| 112 var c = new Candidate<InstanceMirror>(); |
| 113 c.mirror = mirror; |
| 114 c.origin = origin; |
| 115 c.message = message; |
| 116 |
| 117 candidateReceivers.add(c); |
| 118 candidateArguments.add(c); |
| 119 } |
| 120 |
| 121 void addObjectMirror(ObjectMirror mirror) { |
| 122 var c = new Candidate<ObjectMirror>(); |
| 123 c.mirror = mirror; |
| 124 c.origin = null; |
| 125 c.message = null; |
| 126 |
| 127 candidateReceivers.add(c); |
| 128 } |
| 129 |
| 130 void setupInterestingValues() { |
| 131 addInstance(null); |
| 132 addInstance(true); |
| 133 addInstance(false); |
| 134 |
| 135 addInstance([]); |
| 136 addInstance(const []); |
| 137 addInstance({}); |
| 138 addInstance(const {}); |
| 139 |
| 140 addInstance(() => null); |
| 141 |
| 142 addInstance(-1); |
| 143 addInstance(0); |
| 144 addInstance(1); |
| 145 addInstance(2); |
| 146 |
| 147 addInstance(1 << 31); |
| 148 addInstance(1 << 31 + 1); |
| 149 addInstance(1 << 31 - 1); |
| 150 |
| 151 addInstance(1 << 32); |
| 152 addInstance(1 << 32 + 1); |
| 153 addInstance(1 << 32 - 1); |
| 154 |
| 155 addInstance(1 << 63); |
| 156 addInstance(1 << 63 + 1); |
| 157 addInstance(1 << 63 - 1); |
| 158 |
| 159 addInstance(1 << 64); |
| 160 addInstance(1 << 64 + 1); |
| 161 addInstance(1 << 64 - 1); |
| 162 |
| 163 addInstance(-1.0); |
| 164 addInstance(0.0); |
| 165 addInstance(1.0); |
| 166 addInstance(2.0); |
| 167 addInstance(double.NAN); |
| 168 addInstance(double.INFINITY); |
| 169 addInstance(double.NEGATIVE_INFINITY); |
| 170 addInstance(double.MIN_POSITIVE); |
| 171 addInstance(double.MAX_FINITE); |
| 172 |
| 173 addInstance("foo"); // ASCII string |
| 174 addInstance("blåbærgrød"); // Latin1 string |
| 175 addInstance("Îñţérñåţîöñåļîžåţîờñ"); // Unicode string |
| 176 addInstance("𝄞"); // Surrogate pairs |
| 177 addInstance("𝄞"[0]); // Surrogate pairs |
| 178 addInstance("𝄞"[1]); // Surrogate pairs |
| 179 addInstance("\u{0}"); // Non-printing charater |
| 180 addInstance("\u{1}"); // Non-printing charater |
| 181 addInstance("f\u{0}oo"); // Internal NUL |
| 182 addInstance("blåbæ\u{0}rgrød"); // Internal NUL |
| 183 addInstance("Îñţérñåţîö\u{0}ñåļîžåţîờñ"); // Internal NUL |
| 184 addInstance("\u{0}𝄞"); // Internal NUL |
| 185 |
| 186 for (int len = 0; len < 8; len++) { |
| 187 addInstance(fillInt(new Int8List(len))); |
| 188 addInstance(fillInt(new Int16List(len))); |
| 189 addInstance(fillInt(new Int32List(len))); |
| 190 addInstance(fillInt(new Int64List(len))); |
| 191 addInstance(fillInt(new Uint8List(len))); |
| 192 addInstance(fillInt(new Uint16List(len))); |
| 193 addInstance(fillInt(new Uint32List(len))); |
| 194 addInstance(fillInt(new Uint64List(len))); |
| 195 addInstance(fillFloat(new Float32List(len))); |
| 196 addInstance(fillFloat(new Float64List(len))); |
| 197 } |
| 198 |
| 199 randomInstance(ignore) { |
| 200 return randomElementOf(candidateArguments).mirror.reflectee; |
| 201 } |
| 202 for (int len = 0; len < 8; len++) { |
| 203 addInstance(new List.generate(len, randomInstance)); |
| 204 } |
| 205 } |
| 206 |
| 207 void fillInt(TypedData d) { |
| 208 for (var i = 0; i < d.length; i++) { |
| 209 d[i] = random.nextInt(0xFFFFFFFF); |
| 210 } |
| 211 } |
| 212 |
| 213 void fillFloat(TypedData d) { |
| 214 for (var i = 0; i < d.length; i++) { |
| 215 d[i] = random.nextDouble(); |
| 216 } |
| 217 } |
| 218 |
| 219 void setupClasses() { |
| 220 currentMirrorSystem().libraries.values.forEach((lib) { |
| 221 if (lib.simpleName == #fuzzer) return; // Don't recurse. |
| 222 addObjectMirror(lib); |
| 223 lib.declarations.values.forEach((decl) { |
| 224 if (decl is ClassMirror) { |
| 225 addObjectMirror(decl); |
| 226 } |
| 227 }); |
| 228 }); |
| 229 } |
| 230 |
| 231 MethodMirror randomMethodOf(receiver) { |
| 232 if (receiver is ClassMirror) { |
| 233 return randomElementOf(receiver.declarations.values.where( |
| 234 (d) => d is MethodMirror && d.isStatic).toList()); |
| 235 } else if (receiver is LibraryMirror) { |
| 236 return randomElementOf(receiver.declarations.values.where( |
| 237 (d) => d is MethodMirror).toList()); |
| 238 } else if (receiver is InstanceMirror) { |
| 239 var methods = []; |
| 240 var cls = receiver.type; |
| 241 while (cls != reflectClass(Object)) { |
| 242 cls.declarations.values.forEach((d) { |
| 243 if (d is MethodMirror && !d.isStatic) methods.add(d); |
| 244 }); |
| 245 cls = cls.superclass; |
| 246 } |
| 247 return randomElementOf(methods); |
| 248 } |
| 249 throw new Error("UNREACHABLE"); |
| 250 } |
| 251 |
| 252 String prettyMessageName(receiver, method) { |
| 253 var r = "?", m = "?"; |
| 254 if (receiver is InstanceMirror) { |
| 255 r = MirrorSystem.getName(receiver.type.simpleName); |
| 256 } else if (receiver is ClassMirror) { |
| 257 r = MirrorSystem.getName(receiver.simpleName); |
| 258 r = "$r class"; |
| 259 } else if (receiver is LibraryMirror) { |
| 260 r = MirrorSystem.getName(receiver.simpleName); |
| 261 r = "$r lib"; |
| 262 } |
| 263 m = MirrorSystem.getName(method.simpleName); |
| 264 return "$r>>#$m"; |
| 265 } |
| 266 |
| 267 void fuzz(Candidate c) { |
| 268 ObjectMirror receiver = c.mirror; |
| 269 MethodMirror method = randomMethodOf(receiver); |
| 270 if (method == null) return; |
| 271 if (blacklist.contains(MirrorSystem.getName(method.qualifiedName))) return; |
| 272 |
| 273 List positional = randomPositionalArgumentsFor(method); |
| 274 Map named = randomNamedArgumentsFor(method); |
| 275 InstanceMirror result; |
| 276 |
| 277 String message = prettyMessageName(receiver, method); |
| 278 if (trace) { |
| 279 c.trace(); |
| 280 print(message); |
| 281 } |
| 282 |
| 283 if (method.isConstructor) { |
| 284 try { |
| 285 result = receiver.newInstance(method.simpleName, positional, named); |
| 286 } catch(e) {} |
| 287 } else if (method.isRegularMethod) { |
| 288 try { |
| 289 result = receiver.invoke(method.simpleName, positional, named); |
| 290 } catch(e) {} |
| 291 } else if (method.isGetter) { |
| 292 try { |
| 293 result = receiver.getField(method.simpleName); |
| 294 } catch(e) {} |
| 295 } else if (method.isSetter) { |
| 296 try { |
| 297 result = receiver.setField(method.simpleName, positional[0]); |
| 298 } catch(e) {} |
| 299 } |
| 300 |
| 301 if (result != null) { |
| 302 addInstanceMirror(result, c, message); |
| 303 } |
| 304 } |
| 305 |
| 306 InstanceMirror randomArgumentWithBias(TypeMirror bias) { |
| 307 if (maybe(0.75)) { |
| 308 for (var candidate in candidateArguments) { |
| 309 if (candidate.mirror.type.isAssignableTo(bias)) { |
| 310 return candidate.mirror; |
| 311 } |
| 312 } |
| 313 } |
| 314 return randomElementOf(candidateArguments).mirror; |
| 315 } |
| 316 |
| 317 List randomPositionalArgumentsFor(MethodMirror method) { |
| 318 var result = []; |
| 319 for (int i = 0; i < method.parameters.length; i++) { |
| 320 ParameterMirror p = method.parameters[i]; |
| 321 if (!p.isNamed && (!p.isOptional || maybe(0.5))) { |
| 322 result.add(randomArgumentWithBias(p.type)); |
| 323 } |
| 324 } |
| 325 return result; |
| 326 } |
| 327 |
| 328 Map randomNamedArgumentsFor(MethodMirror method) { |
| 329 var result = {}; |
| 330 for (int i = 0; i < method.parameters.length; i++) { |
| 331 ParameterMirror p = method.parameters[i]; |
| 332 if (p.isNamed && maybe(0.5)) { |
| 333 result[p.simpleName] = randomArgumentWithBias(p.type); |
| 334 } |
| 335 } |
| 336 |
| 337 return result; |
| 338 } |
| 339 |
| 340 void garbageCollect() { |
| 341 // Chain a bunch of moderately sized arrays, then let go of them. Using a |
| 342 // moderate size avoids our allocations going directly to a large object |
| 343 // page in old space. |
| 344 var n; |
| 345 for (int i = 0; i < 2048; i++) { |
| 346 var m = new List(512); |
| 347 m[0] = n; |
| 348 n = m; |
| 349 } |
| 350 } |
| OLD | NEW |