Skip to content

Commit b0cacbd

Browse files
authored
Migrate CIFuzz and ClusterFuzzLite to Ubuntu 24.04 (#14382)
## Description This PR migrates CIFuzz and ClusterFuzzLite to support Ubuntu 24.04. This allows projects to opt-in to a newer build environment by adding `base_os_version: ubuntu-24-04` to their `project.yaml`. ## Changes - Created new Dockerfiles for Ubuntu 24.04 support: - `infra/cifuzz/cifuzz-base/ubuntu-24-04.Dockerfile` - `infra/build_fuzzers.ubuntu-24-04.Dockerfile` - `infra/run_fuzzers.ubuntu-24-04.Dockerfile` - Updated `infra/cifuzz/build-images.sh` to build the new images. - Updated `infra/cifuzz/build_fuzzers_entrypoint.py` and `infra/cifuzz/run_fuzzers_entrypoint.py` to support pivoting to Ubuntu 24.04. - Updated `infra/cifuzz/config_utils.py` to handle the new base OS version. - Updated documentation in `docs/getting-started/continuous_integration.md`. ## Evidence Verified the changes by running an external project (zlib) with Ubuntu 24.04. ### Build Log Snippet (Ubuntu 24.04) ``` => [internal] load metadata for gcr.io/oss-fuzz-base/base-builder:ubuntu-24-04 ... => [5/5] COPY run_tests.sh build.sh *_fuzzer.c* /src/ ... Exit code: 0 ``` ### Run Log Snippet ``` #3554 NEW cov: 609 ft: 2821 corp: 466/57Mb lim: 933840 exec/s: 57 rss: 139Mb L: 195443/933840 MS: 3 InsertByte-ChangeASCIIInt-PersAutoDict- DE: "P?\000\000"- ... ```
1 parent 9c2d852 commit b0cacbd

10 files changed

+286
-0
lines changed

docs/getting-started/continuous_integration.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,21 @@ You can checkout CIFuzz configs for OSS-Fuzz projects. Example -
234234
[systemd](https://github.com/systemd/systemd/blob/main/.github/workflows/cifuzz.yml),
235235
[curl](https://github.com/curl/curl/blob/master/.github/workflows/fuzz.yml).
236236

237+
## Ubuntu 24.04 Support
238+
239+
CIFuzz supports building and running fuzzers in an Ubuntu 24.04 environment.
240+
Existing projects will continue to use the legacy environment (Ubuntu 20.04) by default,
241+
preserving current behavior.
242+
243+
To migrate your project to Ubuntu 24.04, add the following line to your `project.yaml`:
244+
245+
```yaml
246+
base_os_version: ubuntu-24-04
247+
```
248+
249+
For OSS-Fuzz projects, this file is located at `projects/<project_name>/project.yaml`.
250+
For external projects (ClusterFuzzLite), this file is typically located at `.clusterfuzzlite/project.yaml`.
251+
237252
## Understanding results
238253

239254
The results of CIFuzz can be found in two different places.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
################################################################################
16+
# Docker image to run fuzzers for CIFuzz (the run_fuzzers action on GitHub
17+
# actions).
18+
19+
FROM gcr.io/oss-fuzz-base/cifuzz-base:ubuntu-24-04
20+
21+
# Python file to execute when the docker container starts up
22+
# We can't use the env var $OSS_FUZZ_ROOT here. Since it's a constant env var,
23+
# just expand to '/opt/oss-fuzz'.
24+
ENTRYPOINT ["python3", "/opt/oss-fuzz/infra/cifuzz/build_fuzzers_entrypoint.py"]
25+
26+
WORKDIR ${OSS_FUZZ_ROOT}/infra
27+
28+
# Update infra source code.
29+
ADD . ${OSS_FUZZ_ROOT}/infra
30+
31+
RUN python3 -m pip install -r ${OSS_FUZZ_ROOT}/infra/cifuzz/requirements.txt

infra/cifuzz/build-images.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,22 @@ OSS_FUZZ_ROOT=$(realpath $INFRA_DIR/..)
2222

2323
# Build cifuzz-base.
2424
docker build --tag gcr.io/oss-fuzz-base/cifuzz-base --file $CIFUZZ_DIR/cifuzz-base/Dockerfile $OSS_FUZZ_ROOT
25+
docker build --tag gcr.io/oss-fuzz-base/cifuzz-base:ubuntu-24-04 --file $CIFUZZ_DIR/cifuzz-base/ubuntu-24-04.Dockerfile $OSS_FUZZ_ROOT
2526

2627
# Build run-fuzzers and build-fuzzers images.
2728
docker build \
2829
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers-test:v1 \
2930
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers:v1 \
3031
--file $INFRA_DIR/build_fuzzers.Dockerfile $INFRA_DIR
32+
docker build \
33+
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers-test:ubuntu-24-04-v1 \
34+
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers:ubuntu-24-04-v1 \
35+
--file $INFRA_DIR/build_fuzzers.ubuntu-24-04.Dockerfile $INFRA_DIR
3136
docker build \
3237
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers:v1 \
3338
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers-test:v1 \
3439
--file $INFRA_DIR/run_fuzzers.Dockerfile $INFRA_DIR
40+
docker build \
41+
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers:ubuntu-24-04-v1 \
42+
--tag gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers-test:ubuntu-24-04-v1 \
43+
--file $INFRA_DIR/run_fuzzers.ubuntu-24-04.Dockerfile $INFRA_DIR

infra/cifuzz/build_fuzzers_entrypoint.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ def build_fuzzers_entrypoint():
2929
"""Builds OSS-Fuzz project's fuzzers for CI tools."""
3030
config = config_utils.BuildFuzzersConfig()
3131

32+
if config.base_os_version == 'ubuntu-24-04':
33+
result = config_utils.pivot_to_ubuntu_24_04(
34+
'build-fuzzers',
35+
'/opt/oss-fuzz/infra/cifuzz/build_fuzzers_entrypoint.py')
36+
if result is not None:
37+
return result
38+
3239
if config.dry_run:
3340
# Sets the default return code on error to success.
3441
returncode = 0
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
################################################################################
16+
17+
FROM gcr.io/oss-fuzz-base/base-runner:ubuntu-24-04
18+
19+
RUN apt-get update && \
20+
apt-get install -y systemd && \
21+
wget https://download.docker.com/linux/ubuntu/dists/noble/pool/stable/amd64/docker-ce-cli_26.0.0-1~ubuntu.24.04~noble_amd64.deb -O /tmp/docker-ce.deb && \
22+
dpkg -i /tmp/docker-ce.deb && \
23+
rm /tmp/docker-ce.deb
24+
25+
ENV PATH=/opt/gcloud/google-cloud-sdk/bin/:$PATH
26+
ENV OSS_FUZZ_ROOT=/opt/oss-fuzz
27+
28+
# Do this step before copying to make rebuilding faster when developing.
29+
COPY ./infra/cifuzz/requirements.txt /tmp/requirements.txt
30+
RUN python3 -m pip install -r /tmp/requirements.txt && rm /tmp/requirements.txt
31+
32+
ADD . ${OSS_FUZZ_ROOT}
33+
# Don't use the default npm location since jazzer.js can break us.
34+
# This means javascript needed by cifuzz/clusterfuzzlite must be executed in
35+
# OSS_FUZZ_ROOT.
36+
RUN cd ${OSS_FUZZ_ROOT} && npm install ${OSS_FUZZ_ROOT}/infra/cifuzz
37+
38+
39+
ENV PYTHONUNBUFFERED=1
40+
41+
# Python file to execute when the docker container starts up.
42+
# We can't use the env var $OSS_FUZZ_ROOT here. Since it's a constant env var,
43+
# just expand to '/opt/oss-fuzz'.
44+
ENTRYPOINT ["python3", "/opt/oss-fuzz/infra/cifuzz/cifuzz_combined_entrypoint.py"]

infra/cifuzz/cloudbuild.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,34 @@ steps:
3838
- '-f'
3939
- infra/run_fuzzers.Dockerfile
4040
- infra
41+
- name: 'gcr.io/cloud-builders/docker'
42+
args:
43+
- build
44+
- '-t'
45+
- gcr.io/oss-fuzz-base/cifuzz-base:ubuntu-24-04-v1
46+
- '-f'
47+
- infra/cifuzz/cifuzz-base/ubuntu-24-04.Dockerfile
48+
- .
49+
- name: 'gcr.io/cloud-builders/docker'
50+
args:
51+
- build
52+
- '-t'
53+
- gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers:ubuntu-24-04-v1
54+
- '-t'
55+
- gcr.io/oss-fuzz-base/cifuzz-build-fuzzers:ubuntu-24-04-v1
56+
- '-f'
57+
- infra/build_fuzzers.ubuntu-24-04.Dockerfile
58+
- infra
59+
- name: 'gcr.io/cloud-builders/docker'
60+
args:
61+
- build
62+
- '-t'
63+
- gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers:ubuntu-24-04-v1
64+
- '-t'
65+
- gcr.io/oss-fuzz-base/cifuzz-run-fuzzers:ubuntu-24-04-v1
66+
- '-f'
67+
- infra/run_fuzzers.ubuntu-24-04.Dockerfile
68+
- infra
4169
images:
4270
- gcr.io/oss-fuzz-base/cifuzz-base
4371
- gcr.io/oss-fuzz-base/cifuzz-base:v1
@@ -49,4 +77,9 @@ images:
4977
- gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers:v1
5078
- gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers
5179
- gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers:v1
80+
- gcr.io/oss-fuzz-base/cifuzz-base:ubuntu-24-04-v1
81+
- gcr.io/oss-fuzz-base/cifuzz-run-fuzzers:ubuntu-24-04-v1
82+
- gcr.io/oss-fuzz-base/cifuzz-build-fuzzers:ubuntu-24-04-v1
83+
- gcr.io/oss-fuzz-base/clusterfuzzlite-run-fuzzers:ubuntu-24-04-v1
84+
- gcr.io/oss-fuzz-base/clusterfuzzlite-build-fuzzers:ubuntu-24-04-v1
5285
timeout: 1800s

infra/cifuzz/config_utils.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,11 @@
2626

2727
import platform_config
2828
import constants
29+
import re
30+
import subprocess
2931

3032
SANITIZERS = ['address', 'memory', 'undefined', 'coverage']
33+
BASE_OS_VERSION_REGEX = re.compile(r'\s*base_os_version\s*:\s*([^\s]+)')
3134

3235
# TODO(metzman): Set these on config objects so there's one source of truth.
3336
DEFAULT_ENGINE = 'libfuzzer'
@@ -196,6 +199,72 @@ def is_coverage(self):
196199
generating a coverage report."""
197200
return self.sanitizer == 'coverage'
198201

202+
@property
203+
def base_os_version(self):
204+
"""Returns the project's base OS version."""
205+
if self.oss_fuzz_project_name:
206+
# Internal/OSS-Fuzz project.
207+
project_yaml_path = os.path.join('/opt/oss-fuzz/projects',
208+
self.oss_fuzz_project_name,
209+
'project.yaml')
210+
else:
211+
# External project.
212+
project_yaml_path = os.path.join(self.project_src_path,
213+
self.build_integration_path,
214+
'project.yaml')
215+
216+
if not os.path.exists(project_yaml_path):
217+
return 'legacy'
218+
219+
with open(project_yaml_path) as file_handle:
220+
content = file_handle.read()
221+
for line in content.splitlines():
222+
match = BASE_OS_VERSION_REGEX.match(line)
223+
if match:
224+
return match.group(1).strip('\'"')
225+
226+
return 'legacy'
227+
228+
229+
def pivot_to_ubuntu_24_04(image_suffix, script_path, check_result=True):
230+
"""Pivots execution to an Ubuntu 24.04 container if needed."""
231+
with open('/etc/os-release') as file_handle:
232+
if '24.04' not in file_handle.read():
233+
logging.info(
234+
'Base OS version is Ubuntu 24.04, but running in a different OS. Pivoting to Ubuntu 24.04 container.'
235+
)
236+
env = os.environ.copy()
237+
# Ensure we don't loop indefinitely.
238+
env['CIFUZZ_PIVOTED'] = '1'
239+
command = [
240+
'docker', 'run', '--rm', '--privileged', '--volumes-from',
241+
os.environ.get('HOSTNAME', ''), '-e', 'CIFUZZ_PIVOTED=1'
242+
]
243+
# Propagate environment variables.
244+
for key, value in os.environ.items():
245+
command.extend(['-e', f'{key}={value}'])
246+
247+
# Use the ubuntu-24-04 version of the image.
248+
command.append('--entrypoint')
249+
command.append('python3')
250+
command.append(
251+
f'gcr.io/oss-fuzz-base/clusterfuzzlite-{image_suffix}:ubuntu-24-04-v1'
252+
)
253+
254+
# Run the same command.
255+
command.append(script_path)
256+
257+
if check_result:
258+
subprocess.check_call(command)
259+
return 0
260+
else:
261+
try:
262+
subprocess.check_call(command)
263+
except subprocess.CalledProcessError as e:
264+
return e.returncode
265+
return 0
266+
return None
267+
199268

200269
def _get_platform_config(cfl_platform):
201270
"""Returns the CI environment object for |cfl_platform|."""

infra/cifuzz/config_utils_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class BaseConfigTest(unittest.TestCase):
2828

2929
def setUp(self):
3030
test_helpers.patch_environ(self)
31+
os.environ['CIFUZZ_TEST'] = '1'
3132

3233
def _create_config(self):
3334
return config_utils.BuildFuzzersConfig()
@@ -95,12 +96,48 @@ def test_validate(self):
9596
config = self._create_config()
9697
self.assertTrue(config.validate())
9798

99+
def test_base_os_version_external(self):
100+
"""Tests that base_os_version is read correctly for external projects."""
101+
os.environ['PROJECT_SRC_PATH'] = '/src'
102+
# Use patch to mock os.path.exists and open for the project.yaml
103+
with mock.patch('os.path.exists', return_value=True) as mock_exists:
104+
with mock.patch(
105+
'builtins.open',
106+
mock.mock_open(
107+
read_data='base_os_version: ubuntu-24-04')) as mock_open:
108+
config = self._create_config()
109+
self.assertEqual(config.base_os_version, 'ubuntu-24-04')
110+
# Verify it looked in the right place (PROJECT_SRC_PATH + .clusterfuzzlite)
111+
expected_path = os.path.join('/src', '.clusterfuzzlite', 'project.yaml')
112+
# We can't easily check the exact path without more mocking, but we can check it was called
113+
mock_open.assert_called()
114+
115+
def test_base_os_version_external_quoted(self):
116+
"""Tests that base_os_version handles quoted values for external projects."""
117+
os.environ['PROJECT_SRC_PATH'] = '/src'
118+
# Use patch to mock os.path.exists and open for the project.yaml
119+
with mock.patch('os.path.exists', return_value=True) as mock_exists:
120+
with mock.patch(
121+
'builtins.open',
122+
mock.mock_open(
123+
read_data='base_os_version: "ubuntu-24-04"')) as mock_open:
124+
config = self._create_config()
125+
self.assertEqual(config.base_os_version, 'ubuntu-24-04')
126+
127+
def test_base_os_version_default(self):
128+
"""Tests that base_os_version defaults to legacy if not present."""
129+
os.environ['PROJECT_SRC_PATH'] = '/src'
130+
with mock.patch('os.path.exists', return_value=False):
131+
config = self._create_config()
132+
self.assertEqual(config.base_os_version, 'legacy')
133+
98134

99135
class BuildFuzzersConfigTest(unittest.TestCase):
100136
"""Tests for BuildFuzzersConfig."""
101137

102138
def setUp(self):
103139
test_helpers.patch_environ(self)
140+
os.environ['CIFUZZ_TEST'] = '1'
104141

105142
def _create_config(self):
106143
return config_utils.BuildFuzzersConfig()
@@ -140,6 +177,7 @@ class RunFuzzersConfigTest(unittest.TestCase):
140177

141178
def setUp(self):
142179
test_helpers.patch_environ(self)
180+
os.environ['CIFUZZ_TEST'] = '1'
143181

144182
def _create_config(self):
145183
return config_utils.RunFuzzersConfig()

infra/cifuzz/run_fuzzers_entrypoint.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ def run_fuzzers_entrypoint():
5252
This action can be added to any OSS-Fuzz project's workflow that uses
5353
Github."""
5454
config = config_utils.RunFuzzersConfig()
55+
56+
if config.base_os_version == 'ubuntu-24-04':
57+
result = config_utils.pivot_to_ubuntu_24_04(
58+
'run-fuzzers',
59+
'/opt/oss-fuzz/infra/cifuzz/run_fuzzers_entrypoint.py',
60+
check_result=not config.dry_run)
61+
if result is not None:
62+
return result
63+
5564
# The default return code when an error occurs.
5665
returncode = 1
5766
if config.dry_run:
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
################################################################################
16+
# Docker image for running fuzzers on CIFuzz (the run_fuzzers action on GitHub
17+
# actions).
18+
19+
FROM gcr.io/oss-fuzz-base/cifuzz-base:ubuntu-24-04
20+
21+
# Python file to execute when the docker container starts up.
22+
# We can't use the env var $OSS_FUZZ_ROOT here. Since it's a constant env var,
23+
# just expand to '/opt/oss-fuzz'.
24+
ENTRYPOINT ["python3", "/opt/oss-fuzz/infra/cifuzz/run_fuzzers_entrypoint.py"]
25+
26+
WORKDIR ${OSS_FUZZ_ROOT}/infra
27+
28+
# Copy infra source code.
29+
ADD . ${OSS_FUZZ_ROOT}/infra
30+
31+
RUN python3 -m pip install -r ${OSS_FUZZ_ROOT}/infra/cifuzz/requirements.txt

0 commit comments

Comments
 (0)