]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bc/tests/afl.py
Update to version 3.1.1
[FreeBSD/FreeBSD.git] / contrib / bc / tests / afl.py
1 #! /usr/bin/python3 -B
2 #
3 # SPDX-License-Identifier: BSD-2-Clause
4 #
5 # Copyright (c) 2018-2020 Gavin D. Howard and contributors.
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are met:
9 #
10 # * Redistributions of source code must retain the above copyright notice, this
11 #   list of conditions and the following disclaimer.
12 #
13 # * Redistributions in binary form must reproduce the above copyright notice,
14 #   this list of conditions and the following disclaimer in the documentation
15 #   and/or other materials provided with the distribution.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29
30 import os
31 import sys
32 import shutil
33 import subprocess
34
35 def usage():
36         print("usage: {} [--asan] dir [results_dir [exe options...]]".format(script))
37         sys.exit(1)
38
39 def check_crash(exebase, out, error, file, type, test):
40         if error < 0:
41                 print("\n{} crashed ({}) on {}:\n".format(exebase, -error, type))
42                 print("    {}".format(test))
43                 print("\nCopying to \"{}\"".format(out))
44                 shutil.copy2(file, out)
45                 print("\nexiting...")
46                 sys.exit(error)
47
48 def run_test(cmd, exebase, tout, indata, out, file, type, test, environ=None):
49         try:
50                 p = subprocess.run(cmd, timeout=tout, input=indata, stdout=subprocess.PIPE,
51                                    stderr=subprocess.PIPE, env=environ)
52                 check_crash(exebase, out, p.returncode, file, type, test)
53         except subprocess.TimeoutExpired:
54                 print("\n    {} timed out. Continuing...\n".format(exebase))
55
56 def create_test(file, tout, environ=None):
57
58         print("    {}".format(file))
59
60         base = os.path.basename(file)
61
62         if base == "README.txt":
63                 return
64
65         with open(file, "rb") as f:
66                 lines = f.readlines()
67
68         print("        Running whole file...")
69
70         run_test(exe + [ file ], exebase, tout, halt.encode(), out, file, "file", file, environ)
71
72         print("        Running file through stdin...")
73
74         with open(file, "rb") as f:
75                 content = f.read()
76
77         run_test(exe, exebase, tout, content, out, file,
78                  "running {} through stdin".format(file), file, environ)
79
80
81 def get_children(dir, get_files):
82         dirs = []
83         with os.scandir(dir) as it:
84                 for entry in it:
85                         if not entry.name.startswith('.') and     \
86                            ((entry.is_dir() and not get_files) or \
87                             (entry.is_file() and get_files)):
88                                 dirs.append(entry.name)
89         dirs.sort()
90         return dirs
91
92 script = sys.argv[0]
93 testdir = os.path.dirname(script)
94
95 if __name__ != "__main__":
96         usage()
97
98 timeout = 2.5
99
100 if len(sys.argv) < 2:
101         usage()
102
103 idx = 1
104
105 exedir = sys.argv[idx]
106
107 asan = (exedir == "--asan")
108
109 if asan:
110         idx += 1
111         if len(sys.argv) < idx + 1:
112                 usage()
113         exedir = sys.argv[idx]
114
115 if len(sys.argv) >= idx + 2:
116         resultsdir = sys.argv[idx + 1]
117 else:
118         if exedir == "bc":
119                 resultsdir = testdir + "/../../results"
120         else:
121                 resultsdir = testdir + "/../../results_dc"
122
123 if len(sys.argv) >= idx + 3:
124         exe = sys.argv[idx + 2]
125 else:
126         exe = testdir + "/../bin/" + exedir
127
128 exebase = os.path.basename(exe)
129
130 if exebase == "bc":
131         halt = "halt\n"
132         options = "-lq"
133 else:
134         halt = "q\n"
135         options = "-x"
136
137 if len(sys.argv) >= idx + 4:
138         exe = [ exe, sys.argv[idx + 3:], options ]
139 else:
140         exe = [ exe, options ]
141 for i in range(4, len(sys.argv)):
142         exe.append(sys.argv[i])
143
144 out = testdir + "/../.test.txt"
145
146 print(os.path.realpath(os.getcwd()))
147
148 dirs = get_children(resultsdir, False)
149
150 if asan:
151         env = os.environ.copy()
152         env['ASAN_OPTIONS'] = 'abort_on_error=1:allocator_may_return_null=1'
153
154 for d in dirs:
155
156         d = resultsdir + "/" + d
157
158         print(d)
159
160         files = get_children(d + "/crashes/", True)
161
162         for file in files:
163                 file = d + "/crashes/" + file
164                 create_test(file, timeout)
165
166         if not asan:
167                 continue
168
169         files = get_children(d + "/queue/", True)
170
171         for file in files:
172                 file = d + "/queue/" + file
173                 create_test(file, timeout * 2, env)
174
175 print("Done")
176