Skip to content
This repository was archived by the owner on Nov 26, 2020. It is now read-only.

Commit d4e0f97

Browse files
committed
Fix MozillaSecurity#169 by adapting its fixes and review suggestions to master.
1 parent 6631724 commit d4e0f97

4 files changed

Lines changed: 85 additions & 69 deletions

File tree

src/funfuzz/js/compare_jit.py

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -61,38 +61,40 @@ def ignore_some_stderr(err_inp):
6161
return lines
6262

6363

64-
def compare_jit(jsEngine, # pylint: disable=invalid-name,missing-param-doc,missing-type-doc,too-many-arguments
64+
def compare_jit(js_engine, # pylint: disable=invalid-name,missing-param-doc,missing-type-doc,too-many-arguments
6565
flags, infilename, logPrefix, repo, build_options_str, targetTime, options):
6666
"""For use in loop.py
6767
6868
Returns:
6969
bool: True if any kind of bug is found, otherwise False
7070
"""
7171
# pylint: disable=too-many-locals
72+
js_engine = Path(js_engine)
73+
infilename = Path(infilename)
7274
# If Lithium uses this as an interestingness test, logPrefix is likely not a Path object, so make it one.
7375
logPrefix = Path(logPrefix)
7476
initialdir_name = (logPrefix.parent / (logPrefix.stem + "-initial"))
75-
# pylint: disable=invalid-name
76-
cl = compareLevel(jsEngine, flags, infilename, initialdir_name, options, False, True)
77+
cl = compareLevel(js_engine, # pylint: disable=invalid-name
78+
flags, infilename, initialdir_name, options, False, True)
7779
lev = cl[0]
7880

7981
if lev != js_interesting.JS_FINE:
8082
itest = [__name__, "--flags=" + " ".join(flags),
81-
"--minlevel=" + str(lev), "--timeout=" + str(options.timeout), options.knownPath]
83+
"--minlevel=" + str(lev), "--timeout=" + str(options.timeout)]
8284
(lithResult, _lithDetails, autoBisectLog) = lithium_helpers.pinpoint( # pylint: disable=invalid-name
83-
itest, logPrefix, jsEngine, [], infilename, repo, build_options_str, targetTime, lev)
85+
itest, logPrefix, js_engine, [], infilename, repo, build_options_str, targetTime, lev)
8486
if lithResult == lithium_helpers.LITH_FINISHED:
85-
print("Retesting %s after running Lithium:" % infilename)
87+
print("Retesting %s after running Lithium:" % str(infilename))
8688
finaldir_name = (logPrefix.parent / (logPrefix.stem + "-final"))
87-
retest_cl = compareLevel(jsEngine, flags, infilename, finaldir_name, options, True, False)
89+
retest_cl = compareLevel(js_engine, flags, infilename, finaldir_name, options, True, False)
8890
if retest_cl[0] != js_interesting.JS_FINE:
89-
cl = retest_cl
91+
cl = retest_cl # pylint: disable=invalid-name
9092
quality = 0
9193
else:
9294
quality = 6
9395
else:
9496
quality = 10
95-
print("compare_jit: Uploading %s with quality %s" % (infilename, quality))
97+
print("compare_jit: Uploading %s with quality %s" % (str(infilename), quality))
9698

9799
metadata = {}
98100
if autoBisectLog:
@@ -103,17 +105,19 @@ def compare_jit(jsEngine, # pylint: disable=invalid-name,missing-param-doc,miss
103105
return False
104106

105107

106-
def compareLevel(jsEngine, flags, infilename, logPrefix, options, showDetailedDiffs, quickMode):
107-
# pylint: disable=invalid-name,missing-docstring,missing-return-doc,missing-return-type-doc,too-complex
108-
# pylint: disable=too-many-branches,too-many-arguments,too-many-locals
108+
def compareLevel(js_engine, # pylint: disable=invalid-name,missing-docstring,missing-return-doc,missing-return-type-doc
109+
flags, infilename, logPrefix, options, showDetailedDiffs, quickMode):
110+
# pylint: disable=too-complex,too-many-branches,too-many-arguments,too-many-locals
109111

110112
# options dict must be one we can pass to js_interesting.ShellResult
111-
# we also use it directly for knownPath, timeout, and collector
113+
# we also use it directly for timeout, and collector
112114
# Return: (lev, crashInfo) or (js_interesting.JS_FINE, None)
113115

114-
assert isinstance(infilename, Path)
116+
js_engine = Path(js_engine)
117+
infilename = Path(infilename)
118+
logPrefix = Path(logPrefix)
115119

116-
combos = shell_flags.basic_flag_sets(jsEngine)
120+
combos = shell_flags.basic_flag_sets(str(js_engine))
117121

118122
if quickMode:
119123
# Only used during initial fuzzing. Allowed to have false negatives.
@@ -122,7 +126,7 @@ def compareLevel(jsEngine, flags, infilename, logPrefix, options, showDetailedDi
122126
if flags:
123127
combos.insert(0, flags)
124128

125-
commands = [[jsEngine] + combo + [str(infilename)] for combo in combos]
129+
commands = [[str(js_engine)] + combo + [str(infilename)] for combo in combos]
126130

127131
for i, command in enumerate(commands):
128132
prefix = (logPrefix.parent / ("%s-r%s" % (logPrefix.stem, str(i))))
@@ -192,8 +196,7 @@ def optionDisabledAsmOnOneSide(): # pylint: disable=invalid-name,missing-docstr
192196
rerunCommand = " ".join(quote(str(x)) for x in ["python -m funfuzz.js.compare_jit",
193197
"--flags=" + " ".join(flags),
194198
"--timeout=" + str(options.timeout),
195-
options.knownPath,
196-
jsEngine,
199+
str(js_engine),
197200
str(infilename.name)])
198201
(summary, issues) = summarizeMismatch(mismatchErr, mismatchOut, prefix0, prefix)
199202
summary = (" " + " ".join(quote(str(x)) for x in commands[0]) + "\n " +
@@ -209,7 +212,7 @@ def optionDisabledAsmOnOneSide(): # pylint: disable=invalid-name,missing-docstr
209212
print(summary)
210213
print()
211214
# Create a crashInfo object with empty stdout, and stderr showing diffs
212-
pc = ProgramConfiguration.fromBinary(jsEngine) # pylint: disable=invalid-name
215+
pc = ProgramConfiguration.fromBinary(str(js_engine)) # pylint: disable=invalid-name
213216
pc.addProgramArguments(flags)
214217
crashInfo = CrashInfo.CrashInfo.fromRawCrashData([], summary, pc) # pylint: disable=invalid-name
215218
return (js_interesting.JS_OVERALL_MISMATCH, crashInfo)
@@ -281,11 +284,10 @@ def parseOptions(args): # pylint: disable=invalid-name
281284
default="",
282285
help="space-separated list of one set of flags")
283286
options, args = parser.parse_args(args)
284-
if len(args) != 3:
285-
raise Exception("Wrong number of positional arguments. Need 3 (knownPath, jsengine, infilename).")
286-
options.knownPath = Path(args[0]).expanduser().resolve()
287-
options.jsengine = Path(args[1]).expanduser().resolve()
288-
options.infilename = Path(args[2]).expanduser().resolve()
287+
if len(args) != 2:
288+
raise Exception("Wrong number of positional arguments. Need 2 (jsengine, infilename).")
289+
options.jsengine = Path(args[0]).expanduser().resolve()
290+
options.infilename = Path(args[1]).expanduser().resolve()
289291
options.flags = options.flagsSpaceSep.split(" ") if options.flagsSpaceSep else []
290292
if not options.jsengine.is_file():
291293
raise OSError("js shell does not exist: " + options.jsengine)
@@ -298,24 +300,37 @@ def parseOptions(args): # pylint: disable=invalid-name
298300
return options
299301

300302

301-
# For use by Lithium and autobisectjs. (autobisectjs calls init multiple times because it changes the js engine name)
302-
def init(args):
303-
global gOptions # pylint: disable=invalid-name,global-statement
304-
gOptions = parseOptions(args)
303+
class Interesting(object):
304+
305+
def __init__(self, interestingness_script=False):
306+
if interestingness_script:
307+
global init, interesting # pylint: disable=global-variable-undefined
308+
init = self.init
309+
interesting = self.interesting
310+
311+
self.args = None
312+
self.real_level = None
313+
314+
# For use by Lithium and autobisectjs.
315+
# (autobisectjs calls init multiple times because it changes the js engine name)
316+
def init(self, args):
317+
self.args = parseOptions(args)
318+
319+
def interesting(self, _args, cwd_prefix): # pylint: disable=missing-docstring,missing-return-doc
320+
# pylint: disable=missing-return-type-doc
321+
self.real_level = compareLevel(
322+
Path(self.args.jsengine), self.args.flags, Path(self.args.infilename),
323+
Path(cwd_prefix), self.args, False, False)[0]
324+
return self.real_level >= self.args.minimumInterestingLevel
305325

306326

307-
# FIXME: _args is unused here, we should check if it can be removed? # pylint: disable=fixme
308-
def interesting(_args, cwd_prefix):
309-
cwd_prefix = Path(cwd_prefix) # Lithium uses this function and cwd_prefix from Lithium is not a Path
310-
actualLevel = compareLevel( # pylint: disable=invalid-name
311-
gOptions.jsengine, gOptions.flags, gOptions.infilename, cwd_prefix, gOptions, False, False)[0]
312-
return actualLevel >= gOptions.minimumInterestingLevel
327+
Interesting(interestingness_script=True)
313328

314329

315330
def main():
316331
options = parseOptions(sys.argv[1:])
317332
print(compareLevel(
318-
options.jsengine, options.flags, options.infilename, # pylint: disable=no-member
333+
Path(options.jsengine), options.flags, Path(options.infilename), # pylint: disable=no-member
319334
Path(tempfile.mkdtemp("compare_jitmain")), options, True, False)[0])
320335

321336

src/funfuzz/js/js_interesting.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666

6767
class ShellResult(object): # pylint: disable=missing-docstring,too-many-instance-attributes,too-few-public-methods
6868

69-
# options dict should include: timeout, knownPath, collector, valgrind, shellIsDeterministic
69+
# options dict should include: timeout, collector, valgrind, shellIsDeterministic
7070
def __init__(self, options, runthis, logPrefix, in_compare_jit): # pylint: disable=too-complex,too-many-branches
7171
# pylint: disable=too-many-locals,too-many-statements
7272

@@ -327,8 +327,7 @@ def parseOptions(args): # pylint: disable=invalid-name,missing-docstring,missin
327327
options, args = parser.parse_args(args)
328328
if len(args) < 2:
329329
raise Exception("Not enough positional arguments")
330-
options.knownPath = args[0]
331-
options.jsengineWithArgs = [Path(args[1]).resolve()] + args[2:-1] + [Path(args[-1]).resolve()]
330+
options.jsengineWithArgs = [Path(args[0]).resolve()] + args[1:-1] + [Path(args[-1]).resolve()]
332331
assert options.jsengineWithArgs[0].is_file() # js shell
333332
assert options.jsengineWithArgs[-1].is_file() # testcase
334333
options.collector = create_collector.make_collector()
@@ -341,24 +340,30 @@ def parseOptions(args): # pylint: disable=invalid-name,missing-docstring,missin
341340
# loop uses parseOptions and ShellResult [with in_compare_jit = False]
342341
# compare_jit uses ShellResult [with in_compare_jit = True]
343342

344-
# For use by Lithium and autobisectjs. (autobisectjs calls init multiple times because it changes the js engine name)
345-
def init(args): # pylint: disable=missing-docstring
346-
global gOptions # pylint: disable=global-statement,invalid-name
347-
gOptions = parseOptions(args)
343+
class Interesting(object): # pylint: disable=missing-docstring
348344

345+
def __init__(self, interestingness_script=False):
346+
if interestingness_script:
347+
global init, interesting # pylint: disable=global-variable-undefined,invalid-name
348+
init = self.init
349+
interesting = self.interesting
349350

350-
# FIXME: _args is unused here, we should check if it can be removed? # pylint: disable=fixme
351-
def interesting(_args, cwd_prefix): # pylint: disable=missing-docstring,missing-return-doc
352-
# pylint: disable=missing-return-type-doc
353-
cwd_prefix = Path(cwd_prefix) # Lithium uses this function and cwd_prefix from Lithium is not a Path
354-
options = gOptions
355-
# options, runthis, logPrefix, in_compare_jit
356-
res = ShellResult(options, options.jsengineWithArgs, cwd_prefix, False)
357-
out_log = (cwd_prefix.parent / (cwd_prefix.stem + "-out")).with_suffix(".txt")
358-
err_log = (cwd_prefix.parent / (cwd_prefix.stem + "-err")).with_suffix(".txt")
359-
truncateFile(out_log, 1000000)
360-
truncateFile(err_log, 1000000)
361-
return res.lev >= gOptions.minimumInterestingLevel
351+
self.args = None
352+
353+
def init(self, args): # pylint: disable=missing-docstring
354+
self.args = parseOptions(args)
355+
356+
def interesting(self, _args, cwd_prefix): # pylint: disable=missing-docstring,missing-return-doc
357+
# pylint: disable=missing-return-type-doc
358+
cwd_prefix = Path(cwd_prefix) # Lithium uses this function, and cwd_prefix from Lithium is not a Path
359+
# options, runthis, logPrefix, in_compare_jit
360+
res = ShellResult(self.args, self.args.jsengineWithArgs, cwd_prefix, False)
361+
truncateFile((cwd_prefix.parent / (cwd_prefix.stem + "-out")).with_suffix(".txt"), 1000000)
362+
truncateFile((cwd_prefix.parent / (cwd_prefix.stem + "-err")).with_suffix(".txt"), 1000000)
363+
return res.lev >= self.args.minimumInterestingLevel
364+
365+
366+
Interesting(interestingness_script=True)
362367

363368

364369
# For direct, manual use

src/funfuzz/js/loop.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,8 @@ def parseOpts(args): # pylint: disable=invalid-name,missing-docstring,missing-r
7575
# lower = less time wasted in timeouts and in compare_jit testcases that are thrown away due to OOMs.
7676
options.timeout = int(args[0])
7777

78-
# FIXME: We can probably remove args[1] # pylint: disable=fixme
79-
options.knownPath = "mozilla-central"
80-
options.jsEngine = Path(args[2])
81-
options.engineFlags = args[3:]
78+
options.js_engine = Path(args[1])
79+
options.engineFlags = args[2:]
8280

8381
return options
8482

@@ -154,10 +152,9 @@ def many_timed_runs(targetTime, wtmpDir, args, collector): # pylint: disable=in
154152
js_interesting_args.append("--timeout=" + str(options.timeout))
155153
if options.valgrind:
156154
js_interesting_args.append("--valgrind")
157-
js_interesting_args.append(options.knownPath)
158-
js_interesting_args.append(options.jsEngine)
155+
js_interesting_args.append(str(options.js_engine))
159156
if options.randomFlags:
160-
engineFlags = shell_flags.random_flag_set(options.jsEngine) # pylint: disable=invalid-name
157+
engineFlags = shell_flags.random_flag_set(str(options.js_engine)) # pylint: disable=invalid-name
161158
js_interesting_args.extend(engineFlags)
162159
js_interesting_args.extend(["-e", "maxRunTime=" + str(options.timeout * (1000 // 2))])
163160
js_interesting_args.extend(["-f", fuzzjs])
@@ -196,9 +193,8 @@ def many_timed_runs(targetTime, wtmpDir, args, collector): # pylint: disable=in
196193
itest.append("--valgrind")
197194
itest.append("--minlevel=" + str(res.lev))
198195
itest.append("--timeout=" + str(options.timeout))
199-
itest.append(options.knownPath)
200196
(lithResult, _lithDetails, autoBisectLog) = lithium_helpers.pinpoint( # pylint: disable=invalid-name
201-
itest, logPrefix, options.jsEngine, engineFlags, reduced_log, options.repo,
197+
itest, logPrefix, str(options.js_engine), engineFlags, reduced_log, options.repo,
202198
options.build_options_str, targetTime, res.lev)
203199

204200
# Upload with final output
@@ -237,7 +233,7 @@ def many_timed_runs(targetTime, wtmpDir, args, collector): # pylint: disable=in
237233
with io.open(str(jitcomparefilename), "w", encoding="utf-8", errors="replace") as f:
238234
f.writelines(linesToCompare)
239235
# pylint: disable=invalid-name
240-
anyBug = compare_jit.compare_jit(options.jsEngine, engineFlags, jitcomparefilename,
236+
anyBug = compare_jit.compare_jit(str(options.js_engine), engineFlags, jitcomparefilename,
241237
logPrefix.parent / (logPrefix.stem + "-cj"), options.repo,
242238
options.build_options_str, targetTime, js_interesting_options)
243239
if not anyBug:

src/funfuzz/util/lithium_helpers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@
4040
LITH_FINISHED, LITH_RETESTED_STILL_INTERESTING, LITH_PLEASE_CONTINUE, LITH_BUSTED) = range(8)
4141

4242

43-
def pinpoint(itest, logPrefix, jsEngine, engineFlags, infilename, # pylint: disable=invalid-name,missing-param-doc
43+
def pinpoint(itest, logPrefix, js_engine, engineFlags, infilename, # pylint: disable=invalid-name,missing-param-doc
4444
bisectRepo, build_options_str, targetTime, suspiciousLevel):
4545
# pylint: disable=missing-return-doc,missing-return-type-doc,missing-type-doc,too-many-arguments,too-many-locals
4646
"""Run Lithium and autobisectjs.
4747
4848
itest must be an array of the form [module, ...] where module is an interestingness module.
49-
The module's "interesting" function must accept [...] + [jsEngine] + engineFlags + infilename
49+
The module's "interesting" function must accept [...] + [js_engine] + engineFlags + infilename
5050
(If it's not prepared to accept engineFlags, engineFlags must be empty.)
5151
"""
52-
lithArgs = itest + [str(jsEngine)] + engineFlags + [str(infilename)] # pylint: disable=invalid-name
52+
lithArgs = itest + [str(js_engine)] + engineFlags + [str(infilename)] # pylint: disable=invalid-name
5353

5454
(lithResult, lithDetails) = reduction_strat( # pylint: disable=invalid-name
5555
logPrefix, infilename, lithArgs, targetTime, suspiciousLevel)
@@ -61,11 +61,11 @@ def pinpoint(itest, logPrefix, jsEngine, engineFlags, infilename, # pylint: dis
6161

6262
# pylint: disable=literal-comparison
6363
if (bisectRepo is not "none" and targetTime >= 3 * 60 * 60 and
64-
build_options_str is not None and testJsShellOrXpcshell(jsEngine) != "xpcshell"):
64+
build_options_str is not None and testJsShellOrXpcshell(js_engine) != "xpcshell"):
6565
autobisectCmd = ( # pylint: disable=invalid-name
6666
[sys.executable, "-u", "-m", "funfuzz.autobisectjs"] +
6767
["-b", build_options_str] +
68-
["-p", " ".join(engineFlags + [infilename])] +
68+
["-p", " ".join(engineFlags + [str(infilename)])] +
6969
["-i"] + itest
7070
)
7171
print(" ".join(quote(str(x)) for x in autobisectCmd))

0 commit comments

Comments
 (0)