blob: 8f0e4e287d213fcc43cf2e2ef14c9cdf76f4c84e [file] [log] [blame]
# 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