| # Copyright 2020 Google LLC |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Utilities for OSS-Fuzz infrastructure.""" |
| |
| import os |
| import re |
| import stat |
| import subprocess |
| |
| import helper |
| |
| ALLOWED_FUZZ_TARGET_EXTENSIONS = ['', '.exe'] |
| FUZZ_TARGET_SEARCH_STRING = 'LLVMFuzzerTestOneInput' |
| VALID_TARGET_NAME = re.compile(r'^[a-zA-Z0-9_-]+$') |
| |
| |
| def chdir_to_root(): |
| """Changes cwd to OSS-Fuzz root directory.""" |
| # Change to oss-fuzz main directory so helper.py runs correctly. |
| if os.getcwd() != helper.OSSFUZZ_DIR: |
| os.chdir(helper.OSSFUZZ_DIR) |
| |
| |
| def execute(command, location=None, check_result=False): |
| """ Runs a shell command in the specified directory location. |
| |
| Args: |
| command: The command as a list to be run. |
| location: The directory the command is run in. |
| check_result: Should an exception be thrown on failed command. |
| |
| Returns: |
| The stdout of the command, the error code. |
| |
| Raises: |
| RuntimeError: running a command resulted in an error. |
| """ |
| |
| if not location: |
| location = os.getcwd() |
| process = subprocess.Popen(command, stdout=subprocess.PIPE, cwd=location) |
| out, err = process.communicate() |
| if check_result and (process.returncode or err): |
| raise RuntimeError('Error: %s\n Command: %s\n Return code: %s\n Out: %s' % |
| (err, command, process.returncode, out)) |
| if out is not None: |
| out = out.decode('ascii').rstrip() |
| return out, process.returncode |
| |
| |
| def get_fuzz_targets(path): |
| """Get list of fuzz targets in a directory. |
| |
| Args: |
| path: A path to search for fuzz targets in. |
| |
| Returns: |
| A list of paths to fuzzers or an empty list if None. |
| """ |
| if not os.path.exists(path): |
| return [] |
| fuzz_target_paths = [] |
| for root, _, _ in os.walk(path): |
| for filename in os.listdir(path): |
| file_path = os.path.join(root, filename) |
| if is_fuzz_target_local(file_path): |
| fuzz_target_paths.append(file_path) |
| |
| return fuzz_target_paths |
| |
| |
| def get_container_name(): |
| """Gets the name of the current docker container you are in. |
| /proc/self/cgroup can be used to check control groups e.g. Docker. |
| See: https://docs.docker.com/config/containers/runmetrics/ for more info. |
| |
| Returns: |
| Container name or None if not in a container. |
| """ |
| with open('/proc/self/cgroup') as file_handle: |
| if 'docker' not in file_handle.read(): |
| return None |
| with open('/etc/hostname') as file_handle: |
| return file_handle.read().strip() |
| |
| |
| def is_fuzz_target_local(file_path): |
| """Returns whether |file_path| is a fuzz target binary (local path). |
| Copied from clusterfuzz src/python/bot/fuzzers/utils.py |
| with slight modifications. |
| """ |
| filename, file_extension = os.path.splitext(os.path.basename(file_path)) |
| if not VALID_TARGET_NAME.match(filename): |
| # Check fuzz target has a valid name (without any special chars). |
| return False |
| |
| if file_extension not in ALLOWED_FUZZ_TARGET_EXTENSIONS: |
| # Ignore files with disallowed extensions (to prevent opening e.g. .zips). |
| return False |
| |
| if not os.path.exists(file_path) or not os.access(file_path, os.X_OK): |
| return False |
| |
| if filename.endswith('_fuzzer'): |
| return True |
| |
| if os.path.exists(file_path) and not stat.S_ISREG(os.stat(file_path).st_mode): |
| return False |
| |
| with open(file_path, 'rb') as file_handle: |
| return file_handle.read().find(FUZZ_TARGET_SEARCH_STRING.encode()) != -1 |