aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Leo Neat <leosneat@gmail.com>2020-02-06 13:39:43 -0800
committerGravatar GitHub <noreply@github.com>2020-02-06 13:39:42 -0800
commit5cefaac000b3b6c5c1e9db908ba8c970dfb55f51 (patch)
tree4f12217dd0c34d52426ba364d5c3c7b09fc7d3bb
parent69f75431fe26e48a03b01599228142cce7da0ab6 (diff)
[CIFuzz] Separate build and run actions (#3336)
* Separating actions into build and run * Formatting * Formatting * Removing project-name from run action * Jonathan comments * Maxs comments pt.1 * Updating example_main.yml * Switch 1 0 to true false * Add base Dockerfile for CIFuzz * Adding license to dockerfile
-rw-r--r--infra/cifuzz/actions/build_fuzzers/Dockerfile24
-rw-r--r--infra/cifuzz/actions/build_fuzzers/action.yml (renamed from infra/cifuzz/actions/action.yml)5
-rw-r--r--infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py (renamed from infra/cifuzz/actions/entrypoint.py)33
-rw-r--r--infra/cifuzz/actions/run_fuzzers/Dockerfile24
-rw-r--r--infra/cifuzz/actions/run_fuzzers/action.yml17
-rw-r--r--infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py92
-rw-r--r--infra/cifuzz/cifuzz-base/Dockerfile (renamed from infra/cifuzz/actions/Dockerfile)7
-rw-r--r--infra/cifuzz/cifuzz.py8
-rw-r--r--infra/cifuzz/cifuzz_test.py9
-rw-r--r--infra/cifuzz/example_main.yml26
-rw-r--r--infra/cifuzz/fuzz_target.py5
11 files changed, 198 insertions, 52 deletions
diff --git a/infra/cifuzz/actions/build_fuzzers/Dockerfile b/infra/cifuzz/actions/build_fuzzers/Dockerfile
new file mode 100644
index 00000000..0e05bcde
--- /dev/null
+++ b/infra/cifuzz/actions/build_fuzzers/Dockerfile
@@ -0,0 +1,24 @@
+# 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.
+#
+################################################################################
+# Docker image to run CIFuzz in.
+
+FROM gcr.io/oss-fuzz-base/cifuzz-base
+
+# Copies your code file from action repository to the container
+COPY build_fuzzers_entrypoint.py /opt/build_fuzzers_entrypoint.py
+
+# Python file to execute when the docker container starts up
+ENTRYPOINT ["python3", "/opt/entrypoint.py"]
diff --git a/infra/cifuzz/actions/action.yml b/infra/cifuzz/actions/build_fuzzers/action.yml
index 72df36d5..cea4c9f4 100644
--- a/infra/cifuzz/actions/action.yml
+++ b/infra/cifuzz/actions/build_fuzzers/action.yml
@@ -5,10 +5,6 @@ inputs:
project-name:
description: 'Name of the corresponding OSS-Fuzz project.'
required: true
- fuzz-seconds:
- description: 'The total time allotted for fuzzing in seconds.'
- required: true
- default: 360
dry-run:
description: 'If set, run the action without actually reporting a failure.'
default: false
@@ -17,5 +13,4 @@ runs:
image: 'Dockerfile'
env:
PROJECT_NAME: ${{ inputs.project-name }}
- FUZZ_SECONDS: ${{ inputs.fuzz-seconds }}
DRY_RUN: ${{ inputs.dry-run}}
diff --git a/infra/cifuzz/actions/entrypoint.py b/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py
index 7d924acf..92d795a3 100644
--- a/infra/cifuzz/actions/entrypoint.py
+++ b/infra/cifuzz/actions/build_fuzzers/build_fuzzers_entrypoint.py
@@ -28,24 +28,26 @@ logging.basicConfig(
def main():
- """Runs OSS-Fuzz project's fuzzers for CI tools.
+ """Build OSS-Fuzz project's fuzzers for CI tools.
This script is used to kick off the Github Actions CI tool. It is the
- entrypoint of the Dockerfile in this directory. This action can be added to
+ entrypoint of the Dockerfile in this directory. This action can be added to
any OSS-Fuzz project's workflow that uses Github.
+ Note: The resulting clusterfuzz binaries of this build are placed in
+ the directory: ${GITHUB_WORKSPACE}/out
+
Required environment variables:
PROJECT_NAME: The name of OSS-Fuzz project.
- FUZZ_TIME: The length of time in seconds that fuzzers are to be run.
GITHUB_REPOSITORY: The name of the Github repo that called this script.
GITHUB_SHA: The commit SHA that triggered this script.
GITHUB_REF: The pull request reference that triggered this script.
GITHUB_EVENT_NAME: The name of the hook event that triggered this script.
+ GITHUB_WORKSPACE: The shared volume directory where input artifacts are.
Returns:
0 on success or 1 on Failure.
"""
oss_fuzz_project_name = os.environ.get('PROJECT_NAME')
- fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 360))
github_repo_name = os.path.basename(os.environ.get('GITHUB_REPOSITORY'))
pr_ref = os.environ.get('GITHUB_REF')
commit_sha = os.environ.get('GITHUB_SHA')
@@ -58,16 +60,6 @@ def main():
# The default return code when an error occurs.
error_code = 1
if dry_run:
- # A testcase file is required in order for CIFuzz to surface bugs.
- # If the file does not exist, the action will crash attempting to upload it.
- # The dry run needs this file because it is set to upload a test case both
- # on successful runs and on failures.
- out_dir = os.path.join(workspace, 'out')
- os.makedirs(out_dir, exist_ok=True)
- file_handle = open(os.path.join(out_dir, 'testcase'), 'w')
- file_handle.write('No bugs detected.')
- file_handle.close()
-
# Sets the default return code on error to success.
error_code = 0
@@ -86,19 +78,6 @@ def main():
logging.error('Error building fuzzers for project %s with pull request %s.',
oss_fuzz_project_name, pr_ref)
return error_code
-
- # Run the specified project's fuzzers from the build.
- run_status, bug_found = cifuzz.run_fuzzers(oss_fuzz_project_name,
- fuzz_seconds, workspace)
- if not run_status:
- logging.error('Error occured while running fuzzers for project %s.',
- oss_fuzz_project_name)
- return error_code
- if bug_found:
- logging.info('Bug found.')
- if not dry_run:
- # Return 2 when a bug was found by a fuzzer causing the CI to fail.
- return 2
return 0
diff --git a/infra/cifuzz/actions/run_fuzzers/Dockerfile b/infra/cifuzz/actions/run_fuzzers/Dockerfile
new file mode 100644
index 00000000..3a5fb8b0
--- /dev/null
+++ b/infra/cifuzz/actions/run_fuzzers/Dockerfile
@@ -0,0 +1,24 @@
+# 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.
+#
+################################################################################
+# Docker image to run CIFuzz run fuzzers action in.
+
+FROM gcr.io/oss-fuzz-base/cifuzz-base
+
+# Copies your code file from action repository to the container
+COPY run_fuzzers_entrypoint.py /opt/run_fuzzers_entrypoint.py
+
+# Python file to execute when the docker container starts up
+ENTRYPOINT ["python3", "/opt/run_fuzzers_entrypoint.py"]
diff --git a/infra/cifuzz/actions/run_fuzzers/action.yml b/infra/cifuzz/actions/run_fuzzers/action.yml
new file mode 100644
index 00000000..c4ce0e49
--- /dev/null
+++ b/infra/cifuzz/actions/run_fuzzers/action.yml
@@ -0,0 +1,17 @@
+# action.yml
+name: 'run-fuzzers'
+description: 'Runs fuzz target binaries for a specified length of time.'
+inputs:
+ fuzz-seconds:
+ description: 'The total time allotted for fuzzing in seconds.'
+ required: true
+ default: 600
+ dry-run:
+ description: 'If set, run the action without actually reporting a failure.'
+ default: false
+runs:
+ using: 'docker'
+ image: 'Dockerfile'
+ env:
+ FUZZ_SECONDS: ${{ inputs.fuzz-seconds }}
+ DRY_RUN: ${{ inputs.dry-run}}
diff --git a/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py b/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py
new file mode 100644
index 00000000..e2bfc5ca
--- /dev/null
+++ b/infra/cifuzz/actions/run_fuzzers/run_fuzzers_entrypoint.py
@@ -0,0 +1,92 @@
+# 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.
+"""Runs specific OSS-Fuzz project's fuzzers for CI tools."""
+import logging
+import os
+import sys
+
+# pylint: disable=wrong-import-position
+# pylint: disable=import-error
+sys.path.append(os.path.join(os.environ['OSS_FUZZ_ROOT'], 'infra', 'cifuzz'))
+import cifuzz
+
+# TODO: Turn default logging to INFO when CIFuzz is stable
+logging.basicConfig(
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
+ level=logging.DEBUG)
+
+
+def main():
+ """Runs OSS-Fuzz project's fuzzers for CI tools.
+ This is the entrypoint for the run_fuzzers github action.
+ This action can be added to any OSS-Fuzz project's workflow that uses Github.
+
+ NOTE: libfuzzer binaries must be located in the ${GITHUB_WORKSPACE}/out
+ directory in order for this action to be used. This action will only fuzz the
+ binary's that are located in that directory. It is reccomended that you add
+ the build_fuzzers action preceding this one.
+
+ NOTE: Any crash report will be in the filepath:
+ ${GITHUB_WORKSPACE}/out/testcase
+ This can be used in parallel with the upload-artifact action to surface the
+ logs.
+
+ Required environment variables:
+ FUZZ_SECONDS: The length of time in seconds that fuzzers are to be run.
+ GITHUB_WORKSPACE: The shared volume directory where input artifacts are.
+ DRY_RUN: If true, no failures will surface.
+
+ Returns:
+ 0 on success or 1 on Failure.
+ """
+ fuzz_seconds = int(os.environ.get('FUZZ_SECONDS', 600))
+ workspace = os.environ.get('GITHUB_WORKSPACE')
+
+ # Check if failures should not be reported.
+ dry_run = (os.environ.get('DRY_RUN').lower() == 'true')
+
+ # The default return code when an error occurs.
+ error_code = 1
+ if dry_run:
+ # A testcase file is required in order for CIFuzz to surface bugs.
+ # If the file does not exist, the action will crash attempting to upload it.
+ # The dry run needs this file because it is set to upload a test case both
+ # on successful runs and on failures.
+ out_dir = os.path.join(workspace, 'out')
+ os.makedirs(out_dir, exist_ok=True)
+ file_handle = open(os.path.join(out_dir, 'testcase'), 'w')
+ file_handle.write('No bugs detected.')
+ file_handle.close()
+
+ # Sets the default return code on error to success.
+ error_code = 0
+
+ if not workspace:
+ logging.error('This script needs to be run in the Github action context.')
+ return error_code
+ # Run the specified project's fuzzers from the build.
+ run_status, bug_found = cifuzz.run_fuzzers(fuzz_seconds, workspace)
+ if not run_status:
+ logging.error('Error occured while running in workspace %s.', workspace)
+ return error_code
+ if bug_found:
+ logging.info('Bug found.')
+ if not dry_run:
+ # Return 2 when a bug was found by a fuzzer causing the CI to fail.
+ return 2
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/infra/cifuzz/actions/Dockerfile b/infra/cifuzz/cifuzz-base/Dockerfile
index 947c3a22..ecb889a0 100644
--- a/infra/cifuzz/actions/Dockerfile
+++ b/infra/cifuzz/cifuzz-base/Dockerfile
@@ -13,7 +13,6 @@
# limitations under the License.
#
################################################################################
-# Docker image to run CIFuzz in.
FROM ubuntu:16.04
@@ -36,9 +35,3 @@ RUN apt-get update && apt-get install docker-ce docker-ce-cli containerd.io -y
ENV OSS_FUZZ_ROOT=/opt/oss-fuzz
RUN git clone https://github.com/google/oss-fuzz.git ${OSS_FUZZ_ROOT}
-
-# Copies your code file from action repository to the container
-COPY entrypoint.py /opt/entrypoint.py
-
-# Command to execute when the docker container starts up
-ENTRYPOINT ["python3", "/opt/entrypoint.py"]
diff --git a/infra/cifuzz/cifuzz.py b/infra/cifuzz/cifuzz.py
index ce7586e5..43aab311 100644
--- a/infra/cifuzz/cifuzz.py
+++ b/infra/cifuzz/cifuzz.py
@@ -26,6 +26,7 @@ import sys
import fuzz_target
# pylint: disable=wrong-import-position
+# pylint: disable=import-error
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import build_specified_commit
import helper
@@ -124,11 +125,10 @@ def build_fuzzers(project_name,
return True
-def run_fuzzers(project_name, fuzz_seconds, workspace):
+def run_fuzzers(fuzz_seconds, workspace):
"""Runs all fuzzers for a specific OSS-Fuzz project.
Args:
- project_name: The name of the OSS-Fuzz project being built.
fuzz_seconds: The total time allotted for fuzzing.
workspace: The location in a shared volume to store a git repo and build
artifacts.
@@ -156,8 +156,8 @@ def run_fuzzers(project_name, fuzz_seconds, workspace):
# Run fuzzers for alotted time.
for fuzzer_path in fuzzer_paths:
- target = fuzz_target.FuzzTarget(project_name, fuzzer_path,
- fuzz_seconds_per_target, out_dir)
+ target = fuzz_target.FuzzTarget(fuzzer_path, fuzz_seconds_per_target,
+ out_dir)
test_case, stack_trace = target.fuzz()
if not test_case or not stack_trace:
logging.info('Fuzzer %s, finished running.', target.target_name)
diff --git a/infra/cifuzz/cifuzz_test.py b/infra/cifuzz/cifuzz_test.py
index 800df1b5..05011ad9 100644
--- a/infra/cifuzz/cifuzz_test.py
+++ b/infra/cifuzz/cifuzz_test.py
@@ -124,7 +124,7 @@ class RunFuzzersIntegrationTest(unittest.TestCase):
tmp_dir,
commit_sha='0b95fe1039ed7c38fea1f97078316bfc1030c523'))
self.assertTrue(os.path.exists(os.path.join(out_path, 'do_stuff_fuzzer')))
- run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 5, tmp_dir)
+ run_success, bug_found = cifuzz.run_fuzzers(5, tmp_dir)
self.assertTrue(run_success)
self.assertTrue(bug_found)
@@ -133,7 +133,7 @@ class RunFuzzersIntegrationTest(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp_dir:
out_path = os.path.join(tmp_dir, 'out')
os.mkdir(out_path)
- run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 5, tmp_dir)
+ run_success, bug_found = cifuzz.run_fuzzers(5, tmp_dir)
self.assertFalse(run_success)
self.assertFalse(bug_found)
@@ -142,14 +142,13 @@ class RunFuzzersIntegrationTest(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp_dir:
out_path = os.path.join(tmp_dir, 'out')
os.mkdir(out_path)
- run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 0, tmp_dir)
+ run_success, bug_found = cifuzz.run_fuzzers(0, tmp_dir)
self.assertFalse(run_success)
self.assertFalse(bug_found)
def test_invalid_out_dir(self):
"""Tests run_fuzzers with an invalid out directory."""
- run_success, bug_found = cifuzz.run_fuzzers(EXAMPLE_PROJECT, 5,
- 'not/a/valid/path')
+ run_success, bug_found = cifuzz.run_fuzzers(5, 'not/a/valid/path')
self.assertFalse(run_success)
self.assertFalse(bug_found)
diff --git a/infra/cifuzz/example_main.yml b/infra/cifuzz/example_main.yml
new file mode 100644
index 00000000..576c84a2
--- /dev/null
+++ b/infra/cifuzz/example_main.yml
@@ -0,0 +1,26 @@
+name: CIFuzz
+
+on: [push]
+
+jobs:
+ Fuzzing:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Build Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ project-name: 'example'
+ dry-run: false
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ fuzz-time: 600
+ dry-run: false
+ - name: Upload Crash
+ uses: actions/upload-artifact@v1
+ if: failure()
+ with:
+ name: fuzzer_testcase
+ path: ./out/testcase
diff --git a/infra/cifuzz/fuzz_target.py b/infra/cifuzz/fuzz_target.py
index 94eace86..cfb01a4e 100644
--- a/infra/cifuzz/fuzz_target.py
+++ b/infra/cifuzz/fuzz_target.py
@@ -35,24 +35,21 @@ class FuzzTarget:
"""A class to manage a single fuzz target.
Attributes:
- project_name: The name of the OSS-Fuzz project the target is associated.
target_name: The name of the fuzz target.
duration: The length of time in seconds that the target should run.
target_path: The location of the fuzz target binary.
"""
- def __init__(self, project_name, target_path, duration, out_dir):
+ def __init__(self, target_path, duration, out_dir):
"""Represents a single fuzz target.
Args:
- project_name: The OSS-Fuzz project of this target.
target_path: The location of the fuzz target binary.
duration: The length of time in seconds the target should run.
out_dir: The location of where the output from crashes should be stored.
"""
self.target_name = os.path.basename(target_path)
self.duration = duration
- self.project_name = project_name
self.target_path = target_path
self.out_dir = out_dir