forked from mirrors/gecko-dev
		
	Bug 1596691 - Add a JSParser target to fuzz-tests. r=jandem
Differential Revision: https://phabricator.services.mozilla.com/D77006
This commit is contained in:
		
							parent
							
								
									97d7ed04a7
								
							
						
					
					
						commit
						f3e4280df1
					
				
					 3 changed files with 190 additions and 0 deletions
				
			
		|  | @ -143,6 +143,9 @@ js/src/jsapi-tests/binast/ | ||||||
| js/src/tests/ | js/src/tests/ | ||||||
| js/src/Y.js | js/src/Y.js | ||||||
| 
 | 
 | ||||||
|  | # Fuzzing code for testing only, targeting the JS shell | ||||||
|  | js/src/fuzz-tests/ | ||||||
|  | 
 | ||||||
| # Uses `#filter substitution` | # Uses `#filter substitution` | ||||||
| mobile/android/app/mobile.js | mobile/android/app/mobile.js | ||||||
| mobile/android/app/geckoview-prefs.js | mobile/android/app/geckoview-prefs.js | ||||||
|  |  | ||||||
							
								
								
									
										83
									
								
								js/src/fuzz-tests/parsing-evaluate.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								js/src/fuzz-tests/parsing-evaluate.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | ||||||
|  | /* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||||
|  | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||||
|  | 
 | ||||||
|  | // This fuzzing target aims to stress the SpiderMonkey parser. However, for
 | ||||||
|  | // this purpose, it does *not* use `parse()` because some past bugs in the
 | ||||||
|  | // parser could only be triggered in the runtime later. Instead, we use
 | ||||||
|  | // the `evaluate` function which parses and runs the code. This brings in
 | ||||||
|  | // other problems like timeouts and permanent side-effects. We try to minimize
 | ||||||
|  | // the amount of permanent side-effects from running the code by running it
 | ||||||
|  | // in a fresh global for each iteration. We also use a special function
 | ||||||
|  | // called `sanitizeGlobal` to remove any harmful shell functions from the
 | ||||||
|  | // global prior to running. Many of these shell functions would otherwise
 | ||||||
|  | // have permanent side-effects of some sort or be disruptive to testing like
 | ||||||
|  | // increasing the amount of timeouts or leak memory. Finally, the target also
 | ||||||
|  | // tries to catch timeouts locally and signal back any timeouts by returning 1
 | ||||||
|  | // from the iteration function.
 | ||||||
|  | 
 | ||||||
|  | // This global will hold the current fuzzing buffer for each iteration.
 | ||||||
|  | var fuzzBuf; | ||||||
|  | 
 | ||||||
|  | loadRelativeToScript("util/sanitize.js"); | ||||||
|  | 
 | ||||||
|  | deterministicgc(true); | ||||||
|  | 
 | ||||||
|  | // Set a default value for timeouts to 1 second, but allow this to
 | ||||||
|  | // be set on the command line as well using -e fuzzTimeout=VAL.
 | ||||||
|  | if (typeof fuzzTimeout === "undefined") { | ||||||
|  |   fuzzTimeout = 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | function JSFuzzIterate() { | ||||||
|  |   try { | ||||||
|  |     let code = String.fromCharCode(...fuzzBuf); | ||||||
|  |     let result = null; | ||||||
|  | 
 | ||||||
|  |     // Create a new global and sanitize it such that its potentially permanent
 | ||||||
|  |     // side-effects are reduced to a minimum.
 | ||||||
|  |     let global = newGlobal(); | ||||||
|  |     sanitizeGlobal(global); | ||||||
|  | 
 | ||||||
|  |     // Work around memory leaks when the hook is not set
 | ||||||
|  |     evaluate(` | ||||||
|  |       setModuleResolveHook(function(module, specifier) { | ||||||
|  |         throw "Module '" + specifier + "' not found"; | ||||||
|  |       }); | ||||||
|  |       setModuleResolveHook = function() {}; | ||||||
|  |     `, { global: global, catchTermination: true });
 | ||||||
|  | 
 | ||||||
|  |     // Start a timer and set a timeout in addition
 | ||||||
|  |     let lfStart = monotonicNow(); | ||||||
|  |     timeout(fuzzTimeout, function() { return false; }); | ||||||
|  | 
 | ||||||
|  |     try { | ||||||
|  |       result = evaluate(code, { global: global, catchTermination: true }); | ||||||
|  |     } catch(exc) { | ||||||
|  |       print(exc); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     timeout(-1); | ||||||
|  |     let lfStop = monotonicNow(); | ||||||
|  | 
 | ||||||
|  |     // Reset some things that could have been altered by the code we ran
 | ||||||
|  |     gczeal(0); | ||||||
|  |     schedulegc(0); | ||||||
|  |     setGCCallback({ action: "majorGC" }); | ||||||
|  |     clearSavedFrames(); | ||||||
|  | 
 | ||||||
|  |     // If we either ended terminating the script, or we took longer than
 | ||||||
|  |     // the timeout set (but timeout didn't kick in), then we return 1 to
 | ||||||
|  |     // signal libFuzzer that the sample just be abandoned.
 | ||||||
|  |     if (result === "terminated" || (lfStop - lfStart > (fuzzTimeout * 1000 + 200))) { | ||||||
|  |       return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  |   } catch(exc) { | ||||||
|  |     print("Caught toplevel exception: " + exc); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								js/src/fuzz-tests/util/sanitize.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								js/src/fuzz-tests/util/sanitize.js
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,104 @@ | ||||||
|  | /* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | ||||||
|  | /* This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||||||
|  | 
 | ||||||
|  | // This function can be used to "sanitize" a new global for fuzzing in such
 | ||||||
|  | // a way that permanent side-effects, hangs and behavior that could be harmful
 | ||||||
|  | // to libFuzzer targets is reduced to a minimum.
 | ||||||
|  | function sanitizeGlobal(g) { | ||||||
|  |   let lfFuncs = { | ||||||
|  |     // Noisy functions (output)
 | ||||||
|  |     backtrace: function() {}, | ||||||
|  |     getBacktrace: function() {}, | ||||||
|  |     help: function() {}, | ||||||
|  |     print: function(s) { return s.toString(); }, | ||||||
|  |     printErr: function(s) { return s.toString(); }, | ||||||
|  |     putstr: function(s) { return s.toString(); }, | ||||||
|  |     stackDump: function() {}, | ||||||
|  |     dumpHeap: function() {}, | ||||||
|  |     dumpScopeChain: function() {}, | ||||||
|  |     dumpObjectWrappers: function() {}, | ||||||
|  |     dumpGCArenaInfo: function() {}, | ||||||
|  |     printProfilerEvents: function() {}, | ||||||
|  | 
 | ||||||
|  |     // Harmful functions (hangs, timeouts, leaks)
 | ||||||
|  |     getLcovInfo: function() {}, | ||||||
|  |     readline: function() {}, | ||||||
|  |     readlineBuf: function() {}, | ||||||
|  |     timeout: function() {}, | ||||||
|  |     quit: function() {}, | ||||||
|  |     interruptIf: function() {}, | ||||||
|  |     terminate: function() {}, | ||||||
|  |     invokeInterruptCallback: function() {}, | ||||||
|  |     setInterruptCallback: function() {}, | ||||||
|  |     intern: function() {}, | ||||||
|  |     evalInWorker: function() {}, | ||||||
|  |     sleep: function() {}, | ||||||
|  |     cacheEntry: function() {}, | ||||||
|  |     streamCacheEntry: function() {}, | ||||||
|  |     createMappedArrayBuffer: function() {}, | ||||||
|  |     wasmCompileInSeparateProcess: function() {}, | ||||||
|  |     gcparam: function() {}, | ||||||
|  |     newGlobal: function() { return g; }, | ||||||
|  | 
 | ||||||
|  |     // Harmful functions (throw)
 | ||||||
|  |     assertEq: function(a,b) { return a.toString() == b.toString(); }, | ||||||
|  |     throwError: function() {}, | ||||||
|  |     reportOutOfMemory: function() {}, | ||||||
|  |     throwOutOfMemory: function() {}, | ||||||
|  |     reportLargeAllocationFailure: function() {}, | ||||||
|  | 
 | ||||||
|  |     // Functions that need limiting
 | ||||||
|  |     gczeal: function(m, f) { return gczeal(m, 100); }, | ||||||
|  |     startgc: function(n, o) { startgc(n > 20 ? 20 : n, o); }, | ||||||
|  |     gcslice: function(n) { gcslice(n > 20 ? 20 : n); }, | ||||||
|  | 
 | ||||||
|  |     // Global side-effects
 | ||||||
|  |     deterministicgc: function() {}, | ||||||
|  |     fullcompartmentchecks: function() {}, | ||||||
|  |     setIonCheckGraphCoherency: function() {}, | ||||||
|  |     enableShellAllocationMetadataBuilder: function() {}, | ||||||
|  |     setTimeResolution: function() {}, | ||||||
|  |     options: function() { return "tracejit,methodjit,typeinfer"; }, | ||||||
|  |     setJitCompilerOption: function() {}, | ||||||
|  |     clearLastWarning: function() {}, | ||||||
|  |     enableSingleStepProfiling: function() {}, | ||||||
|  |     disableSingleStepProfiling: function() {}, | ||||||
|  |     enableGeckoProfiling: function() {}, | ||||||
|  |     enableGeckoProfilingWithSlowAssertions: function() {}, | ||||||
|  |     disableGeckoProfiling: function() {}, | ||||||
|  |     enqueueJob: function() {}, | ||||||
|  |     globalOfFirstJobInQueue: function() {}, | ||||||
|  |     drainJobQueue: function() {}, | ||||||
|  |     setPromiseRejectionTrackerCallback: function() {}, | ||||||
|  |     startTimingMutator: function() {}, | ||||||
|  |     stopTimingMutator: function() {}, | ||||||
|  |     setModuleLoadHook: function() {}, | ||||||
|  |     // Left enabled, as it is required for now to avoid leaks
 | ||||||
|  |     //setModuleResolveHook: function() {},
 | ||||||
|  |     setModuleMetadataHook: function() {}, | ||||||
|  |     setModuleDynamicImportHook: function() {}, | ||||||
|  |     finishDynamicModuleImport: function() {}, | ||||||
|  |     abortDynamicModuleImport: function() {}, | ||||||
|  |     offThreadCompileScript: function() {}, | ||||||
|  |     runOffThreadScript: function() {}, | ||||||
|  |     offThreadCompileModule: function() {}, | ||||||
|  |     finishOffThreadModule: function() {}, | ||||||
|  |     offThreadDecodeScript: function() {}, | ||||||
|  |     runOffThreadDecodedScript: function() {}, | ||||||
|  |     addPromiseReactions: function() {}, | ||||||
|  |     ignoreUnhandledRejections: function() {}, | ||||||
|  |     enableTrackAllocations: function() {}, | ||||||
|  |     disableTrackAllocations: function() {}, | ||||||
|  |     startTraceLogger: function() {}, | ||||||
|  |     stopTraceLogger: function() {}, | ||||||
|  |     setTestFilenameValidationCallback: function() {}, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   for (let lfFunc in lfFuncs) { | ||||||
|  |     g[lfFunc] = lfFuncs[lfFunc]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return g; | ||||||
|  | } | ||||||
		Loading…
	
		Reference in a new issue
	
	 Christian Holler
						Christian Holler