|
| 1 | +# this file is to automatically generate bug reports |
| 2 | + |
| 3 | +import os |
| 4 | +import re |
| 5 | +import json |
| 6 | +import time |
| 7 | +import html |
| 8 | +import signal |
| 9 | +from reduce import reduce_php |
| 10 | + |
| 11 | + |
| 12 | +def handler(signum, frame): |
| 13 | + raise Exception("end of time") |
| 14 | + |
| 15 | +test_root = "/home/phpfuzz/WorkSpace/flowfusion" |
| 16 | + |
| 17 | +plain_text_bug_report = """ |
| 18 | +================ |
| 19 | +PHP Bug Report |
| 20 | +
|
| 21 | +**PHP Commit:** |
| 22 | +{php_commit} |
| 23 | +
|
| 24 | +**Compiling Flags:** |
| 25 | +{php_config} |
| 26 | +
|
| 27 | +**Crash Site:** |
| 28 | +{crashsite} |
| 29 | +
|
| 30 | +**Keywords:** |
| 31 | +{keyword} |
| 32 | +
|
| 33 | +**Reproducing config:** |
| 34 | +{reducedconfig} |
| 35 | +
|
| 36 | +**Reproducing PHP (best-effort reduced):** |
| 37 | +{reducedphp} |
| 38 | +
|
| 39 | +**Output:** |
| 40 | +{bugout} |
| 41 | +
|
| 42 | +**Reproducing PHP:** |
| 43 | +{bugphp} |
| 44 | +
|
| 45 | +**Reproducing PHPT:** |
| 46 | +{bugphpt} |
| 47 | +
|
| 48 | +**This report is automatically generated via FlowFusion** |
| 49 | +================ |
| 50 | +""" |
| 51 | + |
| 52 | + |
| 53 | +# copy dependencies for reproducing |
| 54 | +if os.path.exists("/tmp/flowfusion_reproducing/")==False: |
| 55 | + os.mkdir("/tmp/flowfusion_reproducing/") |
| 56 | +os.system(f"cp -R {test_root}/phpt_deps/* /tmp/flowfusion_reproducing/") |
| 57 | + |
| 58 | +# Change directory to the "bugs" folder |
| 59 | +os.chdir(f"{test_root}/bugs") |
| 60 | + |
| 61 | +# Find all '.out' files, search for 'Sanitizer' (excluding 'leak') and store the results in a log file |
| 62 | +os.system("find ./ -name '*.out' | xargs grep -E 'Sanitizer|Assertion ' | grep -v 'leak' > /tmp/flowfusion_bug.log") |
| 63 | + |
| 64 | +os.chdir(f"{test_root}") |
| 65 | + |
| 66 | +print("Filtering finished") |
| 67 | + |
| 68 | +# Initialize lists to store unique bug identifiers and bug information |
| 69 | +identifiers = [] |
| 70 | +bugs_info = [] |
| 71 | + |
| 72 | +if not os.path.exists(f'{test_root}/bug_reports/'): |
| 73 | + os.mkdir(f'{test_root}/bug_reports/') |
| 74 | + |
| 75 | +if os.path.exists(f'{test_root}/bug_reports/bugs.json'): |
| 76 | + with open(f'{test_root}/bug_reports/bugs.json', 'r') as file: |
| 77 | + bugs_info = json.load(file) |
| 78 | + identifiers = [bug['identifier'] for bug in bugs_info] |
| 79 | + |
| 80 | +for each_existing_bug in bugs_info: |
| 81 | + each_existing_bug['new'] = 0 |
| 82 | + |
| 83 | +# Read the contents of the bug log file |
| 84 | +with open('/tmp/flowfusion_bug.log', 'r') as f: |
| 85 | + bugs = f.read().strip('\n').split('\n') |
| 86 | + |
| 87 | +# Regular expression to extract identifier patterns from the log |
| 88 | +identifier_pattern = r"(\/php-src\/[^:]+:\d+)" |
| 89 | + |
| 90 | + |
| 91 | +# last_modified_time = os.path.getmtime(file_path) |
| 92 | + |
| 93 | +# Loop through each bug entry in the log |
| 94 | +for eachbug in bugs: |
| 95 | + # Search for the identifier using the regular expression |
| 96 | + identifier = re.search(identifier_pattern, eachbug) |
| 97 | + if identifier: |
| 98 | + identifier = identifier.group() |
| 99 | + # If the identifier is new, add it to the identifiers list and create a bug entry |
| 100 | + if identifier not in identifiers: |
| 101 | + identifiers.append(identifier) |
| 102 | + bug_folder = eachbug.split('/')[1] |
| 103 | + last_modified_time = os.path.getmtime(f"{test_root}/bugs/{bug_folder}") |
| 104 | + readable_time = time.ctime(last_modified_time) |
| 105 | + bugs_info.append({ |
| 106 | + "bugID": len(bugs_info) + 1, # Assign a unique ID to each bug |
| 107 | + "identifier": identifier, # Store the identifier (file path and line number) |
| 108 | + "details": [eachbug.split('/')[1]], |
| 109 | + "mtime": readable_time, |
| 110 | + "new": 1 |
| 111 | + }) |
| 112 | + else: |
| 113 | + # If the identifier already exists, update the existing bug entry |
| 114 | + bug_idx = identifiers.index(identifier) |
| 115 | + bug_folder = eachbug.split('/')[1] |
| 116 | + mtime = bugs_info[bug_idx]["mtime"] |
| 117 | + parsed_time = time.strptime(mtime, "%a %b %d %H:%M:%S %Y") |
| 118 | + # Convert struct_time to a timestamp (seconds since epoch) |
| 119 | + timestamp = time.mktime(parsed_time) |
| 120 | + last_modified_time = os.path.getmtime(f"{test_root}/bugs/{bug_folder}") |
| 121 | + if last_modified_time > timestamp: |
| 122 | + readable_time = time.ctime(last_modified_time) |
| 123 | + bugs_info[bug_idx]["mtime"] = readable_time |
| 124 | + |
| 125 | +# Convert the bug information into a JSON format for further processing |
| 126 | +# Load the list of bug information into a JSON-compatible Python dictionary |
| 127 | +data = json.loads(str(bugs_info).replace("'", '"')) |
| 128 | + |
| 129 | +# Pretty-print the JSON data to a file for easy readability |
| 130 | +with open(f'{test_root}/bug_reports/bugs.json', 'w') as file: |
| 131 | + json.dump(data, file, indent=4) |
| 132 | + |
| 133 | +#with open("/tmp/flowfusion-php-commit","r") as file: |
| 134 | +# php_commit = file.read() |
| 135 | + |
| 136 | +php_commit = "test" |
| 137 | + |
| 138 | +#with open(f"{test_root}/php-src/config.log","r") as file: |
| 139 | +# while True: |
| 140 | +# line = file.readline() |
| 141 | +# if "./configure" in line: |
| 142 | +# php_config = line.strip(' ').strip('$') |
| 143 | +# break |
| 144 | + |
| 145 | +php_config = "test" |
| 146 | + |
| 147 | +with open(f"{test_root}/bug_reports/bugs.json", 'r') as file: |
| 148 | + data = json.load(file) |
| 149 | + |
| 150 | +if os.path.exists(f"{test_root}/bugs")==False: |
| 151 | + print("Please run in flowfusion folder") |
| 152 | + exit() |
| 153 | + |
| 154 | +errors = ["stack-overflow","stack-underflow","heap-buffer-overflow","null pointer","integer overflow","heap-use-after-free","SEGV","core dumped"] |
| 155 | + |
| 156 | +# Accessing the parsed data |
| 157 | +for bug in data: |
| 158 | + upload_bug_folder_name = bug['identifier'].split('/php-src/')[1].replace('/','_').replace('.','_').replace(':','_') |
| 159 | + # if bug['new']==0 and os.path.exists(f"{test_root}/../flowfusion-php.github.io/{upload_bug_folder_name}"): |
| 160 | + # # sed -i -E 's/this bug has been detected for [0-9]+ times/this bug has been detected for 2 times/g' ./sapi_phpdbg_phpdbg_bp_c_132/index.html |
| 161 | + # continue |
| 162 | + print(f"analyzing and uploading {upload_bug_folder_name}") |
| 163 | + bug_folder = f"./bugs/{bug['details'][0]}/" |
| 164 | + |
| 165 | + # get bugout |
| 166 | + f = open(f"{bug_folder}/test.out", "r", encoding="iso_8859_1") |
| 167 | + bugout = f.read() |
| 168 | + f.close() |
| 169 | + |
| 170 | + # get keywords |
| 171 | + keywords = [] |
| 172 | + for error in errors: |
| 173 | + if error in bugout: |
| 174 | + keywords.append(error) |
| 175 | + |
| 176 | + dangerous = 0 |
| 177 | + # if "heap-buffer-overflow" in keywords or "heap-use-after-free" in keywords: |
| 178 | + # dangerous = 1 |
| 179 | + |
| 180 | + # get bugphp |
| 181 | + f = open(f"{bug_folder}/test.php", "r") |
| 182 | + bugphp = f.read() |
| 183 | + f.close() |
| 184 | + |
| 185 | + # get bugphpt |
| 186 | + f = open(f"{bug_folder}/test.phpt", "r") |
| 187 | + bugphpt = f.read() |
| 188 | + f.close() |
| 189 | + |
| 190 | + # get bugsh |
| 191 | + f = open(f"{bug_folder}/test.sh", "r") |
| 192 | + bugsh = f.read() |
| 193 | + f.close() |
| 194 | + |
| 195 | + bug_outputs = ["UndefinedBehaviorSanitizer: undefined-behavior", "AddressSanitizer", "core dumped"] |
| 196 | + # get reducedphp |
| 197 | + os.system(f"cp {bug_folder}/test.php /tmp/flowfusion_reproducing/") |
| 198 | + bug_output = "" |
| 199 | + for each in bug_outputs: |
| 200 | + if each in bugout: |
| 201 | + bug_output = each |
| 202 | + break |
| 203 | + bug_config = "" |
| 204 | + for eachline in bugsh.split('\n'): |
| 205 | + if "gdb --args" in eachline: |
| 206 | + bug_config = eachline.split(' -d ')[1:] |
| 207 | + bug_config[-1] = bug_config[-1].split(' -f ')[0] |
| 208 | + bug_config = ' -d '+' -d '.join(bug_config) |
| 209 | + break |
| 210 | + |
| 211 | + signal.signal(signal.SIGALRM, handler) |
| 212 | + # set 5 mins for reducing one bug |
| 213 | + signal.alarm(300) |
| 214 | + try: |
| 215 | + reducedphp, reduced_config = reduce_php( |
| 216 | + testpath = "/tmp/flowfusion_reproducing/test.php", |
| 217 | + phppath = f"{test_root}/php-src/sapi/cli/php", |
| 218 | + config = bug_config, |
| 219 | + bug_output = bug_output |
| 220 | + ) |
| 221 | + except: |
| 222 | + reducedphp = 'reducing timeout ..' |
| 223 | + reduced_config = 'reducing timeout ..' |
| 224 | + |
| 225 | + bug_report = plain_text_bug_report.format( |
| 226 | + php_commit = php_commit, |
| 227 | + php_config = php_config, |
| 228 | + crashsite = bug['identifier'], |
| 229 | + keyword = str(keywords), |
| 230 | + bugout = bugout, |
| 231 | + bugphp = bugphp, |
| 232 | + bugphpt = bugphpt, |
| 233 | + reducedconfig = reduced_config, |
| 234 | + reducedphp = reducedphp |
| 235 | + ) |
| 236 | + |
| 237 | + f = open(f"{test_root}/bug_reports/{upload_bug_folder_name}.md", "w") |
| 238 | + f.write(bug_report) |
| 239 | + f.close() |
| 240 | + |
| 241 | + |
0 commit comments