Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/getting-started/new_project_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ This configuration file stores project metadata. The following attributes are su
- [builds_per_day](#build_frequency) (optional)
- [file_github_issue](#file_github_issue) (optional)
- [disable_remediation](#disable_remediation) (optional)
- [disable_remediation_pre_review](#disable_remediation_pre_review) (optional)

### homepage
You project's homepage.
Expand Down Expand Up @@ -218,6 +219,13 @@ changes. If enabled (default), proposed code changes and comments to remediate
bugs may be automatically included in disclosure that is private during the
embargo of each issue on a case-by-case basis basis.

### disable_remediation_pre_review (optional) {#disable_remediation_pre_review}
Opt out of human-in-the-loop pre-review for proposed remediation code changes.
If this is enabled, proposed code changes will be included in disclosure
notifications without prior manual review by the OSS-Fuzz team. If disabled
(default), all proposed remediation changes will be reviewed by a human before
being shared.

## Dockerfile {#dockerfile}

This configuration file defines the Docker image for your project. Your [build.sh](#buildsh) script will be executed in inside the container you define.
Expand Down
1 change: 1 addition & 0 deletions infra/presubmit.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ class ProjectYamlChecker:
'coverage_extra_args',
'disabled',
'disable_remediation',
'disable_remediation_pre_review',
'fuzzing_engines',
'help_url',
'homepage',
Expand Down
8 changes: 5 additions & 3 deletions projects/angle/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ RUN apt-get update && \
apt-get install --no-install-recommends -y \
lsb-release sudo pkg-config file

RUN git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
RUN git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
ENV PATH=/src/depot_tools:$PATH
WORKDIR angle
RUN fetch angle
# Increased retries and added more robust fetch/sync
RUN for i in {1..20}; do fetch --nohooks angle && break || (echo "Fetch failed, retrying in 60s..." && sleep 60 && if [ $i -eq 20 ]; then exit 1; fi); done
RUN for i in {1..30}; do gclient sync --jobs 1 --no-history --shallow && break || (echo "Sync failed, retrying in 60s..." && sleep 60 && if [ $i -eq 30 ]; then exit 1; fi); done

RUN ./build/install-build-deps.sh --no-prompt

COPY build.sh *.cc fuzzer_profile $SRC/

# Add fuzzer profile
RUN cat $SRC/fuzzer_profile >> $SRC/angle/BUILD.gn
RUN cat $SRC/fuzzer_profile >> /src/angle/BUILD.gn
25 changes: 23 additions & 2 deletions projects/angle/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,25 @@ if [ "$SANITIZER" = "coverage" ] || [ "$SANITIZER" = "introspector" ] || [ "$SAN
export SANITIZER="address"
fi

# Hack to fix clang version mismatch
# The build system somehow thinks clang version is 23, but it's 22 in /usr/local
if [ ! -d /usr/local/lib/clang/23 ]; then
ln -s /usr/local/lib/clang/22 /usr/local/lib/clang/23 || true
fi

# Remove flags not supported by OSS-Fuzz's clang version
sed -i '/-fdiagnostics-show-inlining-chain/d' build/config/compiler/BUILD.gn
sed -i '/-fno-lifetime-dse/d' build/config/compiler/BUILD.gn
sed -i '/-Wa,--crel,--allow-experimental-crel/d' build/config/compiler/BUILD.gn
# Also handle the ubsan ignore flags in sanitizers.gni
sed -i '/-fsanitize-ignore-for-ubsan-feature=${invoker.sanitizer}/d' build/config/sanitizers/sanitizers.gni

# Configure arguments for gn build
ARGS="treat_warnings_as_errors=false is_component_build=false libcxx_is_shared=false is_debug=false"
ARGS+=" use_custom_libcxx=false use_sysroot=true ozone_platform_x11=false"
ARGS+=" use_custom_libcxx=true use_sysroot=true ozone_platform_x11=false"
ARGS+=" is_clang=true clang_use_chrome_plugins=false clang_base_path=\"/usr/local\""
# Enable MSL and WGSL translators specifically, without enabling the full Metal renderer
ARGS+=" angle_enable_msl=true angle_enable_wgpu=true"

# Configure arguments for gn build
if [ "$SANITIZER" = "undefined" ]; then
Expand All @@ -38,14 +53,20 @@ cp $SRC/*.cc src/fuzz/

# Generate ninja file for build
gn gen out/fuzz --args="$ARGS"
echo $SANITIZER

# Build binary
autoninja -C out/fuzz fuzz_sha1
autoninja -C out/fuzz fuzz_translator
autoninja -C out/fuzz fuzz_spirv_transform
autoninja -C out/fuzz fuzz_spirv_parser
autoninja -C out/fuzz fuzz_preprocessor

# Copy binary to $OUT
cp ./out/fuzz/fuzz_sha1 $OUT
cp ./out/fuzz/fuzz_translator $OUT
cp ./out/fuzz/fuzz_spirv_transform $OUT
cp ./out/fuzz/fuzz_spirv_parser $OUT
cp ./out/fuzz/fuzz_preprocessor $OUT

# Reset sanitizer
if [ -n "$ORIGINAL_SANITIZER" ]; then
Expand Down
89 changes: 89 additions & 0 deletions projects/angle/fuzz_preprocessor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2026 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.
//
////////////////////////////////////////////////////////////////////////////////

#include <fuzzer/FuzzedDataProvider.h>
#include <vector>

#include "compiler/preprocessor/Preprocessor.h"
#include "compiler/preprocessor/DiagnosticsBase.h"
#include "compiler/preprocessor/DirectiveHandlerBase.h"
#include "compiler/preprocessor/Token.h"

using namespace angle::pp;

namespace
{

class DoNothingDiagnostics : public Diagnostics
{
public:
void print(ID id, const SourceLocation &loc, const std::string &text) override {}
};

class DoNothingDirectiveHandler : public DirectiveHandler
{
public:
void handleError(const SourceLocation &loc, const std::string &msg) override {}
void handlePragma(const SourceLocation &loc,
const std::string &name,
const std::string &value,
bool stdgl) override {}
void handleExtension(const SourceLocation &loc,
const std::string &name,
const std::string &behavior) override {}
void handleVersion(const SourceLocation &loc, int version, ShShaderSpec spec, MacroSet *macro_set) override {}
};

} // namespace

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size < 1) return 0;

FuzzedDataProvider fuzzedData(data, size);

ShShaderSpec spec = static_cast<ShShaderSpec>(fuzzedData.ConsumeIntegralInRange<int>(0, 5)); // SH_GLES3_2_SPEC is 5
WebGLExtensionDisableBehavior behavior = fuzzedData.ConsumeBool() ? WebGLExtensionDisableBehavior::Standard : WebGLExtensionDisableBehavior::AnywhereInShader;

PreprocessorSettings settings(spec, behavior);
settings.maxMacroExpansionDepth = fuzzedData.ConsumeIntegralInRange<int>(0, 1000);

DoNothingDiagnostics diagnostics;
DoNothingDirectiveHandler directiveHandler;
Preprocessor preprocessor(&diagnostics, &directiveHandler, settings);

std::vector<uint8_t> remainingData = fuzzedData.ConsumeRemainingBytes<uint8_t>();
if (remainingData.empty()) return 0;

// Ensure null termination
remainingData.push_back(0);

const char *strings[] = { reinterpret_cast<const char *>(remainingData.data()) };
if (!preprocessor.init(1, strings, nullptr))
{
return 0;
}

Token token;
// Lex up to 1000 tokens to avoid infinite loops or very long execution
for (int i = 0; i < 1000; ++i)
{
preprocessor.lex(&token);
if (token.type == Token::LAST) break;
}

return 0;
}
4 changes: 2 additions & 2 deletions projects/angle/fuzz_sha1.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size <= 32 || size > 1024) return 0;

angle::base::SecureHashAlgorithm sha;
sha.Update(data, size);
sha.Final();
(void)sha.Digest();

return 0;
}
113 changes: 113 additions & 0 deletions projects/angle/fuzz_spirv_parser.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2026 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.
//
////////////////////////////////////////////////////////////////////////////////

// fuzz_spirv_parser.cc: A libfuzzer fuzzer for the autogenerated SPIR-V instruction parser.

#include <cstddef>
#include <cstdint>
#include <vector>

#include "common/spirv/spirv_instruction_parser_autogen.h"
#include "common/spirv/spirv_types.h"

using namespace angle::spirv;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
// SPIR-V instructions are at least one word (4 bytes)
if (size < 4)
{
return 0;
}

// Ensure size is a multiple of 4
size_t numWords = size / 4;
const uint32_t *instructions = reinterpret_cast<const uint32_t *>(data);

size_t currentWord = 0;

// Skip SPIR-V header if it looks like one (optional but helps hitting the parser)
if (numWords > kHeaderIndexInstructions && instructions[kHeaderIndexMagic] == spv::MagicNumber)
{
currentWord = kHeaderIndexInstructions;
}

while (currentWord < numWords)
{
const uint32_t *instruction = &instructions[currentWord];
spv::Op opCode;
uint32_t wordCount;

GetInstructionOpAndLength(instruction, &opCode, &wordCount);

if (wordCount == 0 || currentWord + wordCount > numWords)
{
break;
}

// We can't easily call all Parse* functions because they require specific arguments.
// But GetInstructionOpAndLength already covers some logic.
// We can try to call some common ones if the opCode matches.

switch (opCode)
{
case spv::OpName:
{
IdRef target;
LiteralString name;
ParseName(instruction, &target, &name);
break;
}
case spv::OpTypeVoid:
{
IdResult id;
ParseTypeVoid(instruction, &id);
break;
}
case spv::OpTypeFloat:
{
IdResult id;
LiteralInteger width;
spv::FPEncoding encoding;
ParseTypeFloat(instruction, &id, &width, &encoding);
break;
}
// Add more common ones to increase coverage
case spv::OpTypeInt:
{
IdResult id;
LiteralInteger width;
LiteralInteger signedness;
ParseTypeInt(instruction, &id, &width, &signedness);
break;
}
case spv::OpTypeVector:
{
IdResult id;
IdRef componentType;
LiteralInteger componentCount;
ParseTypeVector(instruction, &id, &componentType, &componentCount);
break;
}
default:
break;
}

currentWord += wordCount;
}

return 0;
}
80 changes: 80 additions & 0 deletions projects/angle/fuzz_spirv_transform.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2026 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.
//
////////////////////////////////////////////////////////////////////////////////
// fuzz_spirv_transform.cc: A libfuzzer fuzzer for SPIR-V transformations in the Vulkan backend.

#include <fuzzer/FuzzedDataProvider.h>
#include <vector>

#include "common/spirv/spirv_types.h"
#include "libANGLE/renderer/vulkan/spv_utils.h"
#include "libANGLE/renderer/vulkan/ShaderInterfaceVariableInfoMap.h"

using namespace rx;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
if (size < 10) return 0;

FuzzedDataProvider fuzzedData(data, size);

SpvTransformOptions options;
options.shaderType = static_cast<gl::ShaderType>(fuzzedData.ConsumeIntegralInRange<int>(0, static_cast<int>(gl::ShaderType::EnumCount) - 1));
options.isLastPreFragmentStage = fuzzedData.ConsumeBool();
options.isTransformFeedbackStage = fuzzedData.ConsumeBool();
options.isTransformFeedbackEmulated = fuzzedData.ConsumeBool();
options.isMultisampledFramebufferFetch = fuzzedData.ConsumeBool();
options.enableSampleShading = fuzzedData.ConsumeBool();
options.validate = fuzzedData.ConsumeBool();
options.useSpirvVaryingPrecisionFixer = fuzzedData.ConsumeBool();
options.removeDepthStencilInput = fuzzedData.ConsumeBool();

ShaderInterfaceVariableInfoMap variableInfoMap;

// Add some random variable info
uint32_t numVars = fuzzedData.ConsumeIntegralInRange<uint32_t>(0, 10);
for (uint32_t i = 0; i < numVars; ++i)
{
uint32_t id = fuzzedData.ConsumeIntegralInRange<uint32_t>(1, 100);
ShaderInterfaceVariableInfo &info = variableInfoMap.add(options.shaderType, id);
info.descriptorSet = fuzzedData.ConsumeIntegralInRange<uint32_t>(0, 3);
info.binding = fuzzedData.ConsumeIntegralInRange<uint32_t>(0, 16);
info.location = fuzzedData.ConsumeIntegralInRange<uint32_t>(0, 16);
info.component = fuzzedData.ConsumeIntegralInRange<uint32_t>(0, 3);
}

// The remaining data is the SPIR-V blob
std::vector<uint8_t> remainingData = fuzzedData.ConsumeRemainingBytes<uint8_t>();
if (remainingData.size() % 4 != 0)
{
remainingData.resize(remainingData.size() - (remainingData.size() % 4));
}

if (remainingData.empty()) return 0;

angle::spirv::Blob initialSpirvBlob;
for (size_t i = 0; i < remainingData.size(); i += 4)
{
uint32_t word;
memcpy(&word, &remainingData[i], 4);
initialSpirvBlob.push_back(word);
}

angle::spirv::Blob spirvBlobOut;
// We don't care about the result, just want to see if it crashes.
SpvTransformSpirvCode(options, variableInfoMap, initialSpirvBlob, &spirvBlobOut);

return 0;
}
Loading
Loading