diff --git a/.bazelrc b/.bazelrc index 1246d336b..ec870b10a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -43,3 +43,16 @@ build:asan --linkopt=-fuse-ld=lld try-import %workspace%/clang.bazelrc try-import %workspace%/user.bazelrc try-import %workspace%/local_tsan.bazelrc + +# Configuration used by OSS-Fuzz (https://github.com/google/oss-fuzz) when +# building the //fuzz targets. The clang environment settings are scoped to +# this configuration so that normal builds are unaffected. +build:oss-fuzz --action_env=CC=clang +build:oss-fuzz --action_env=CXX=clang++ +build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:oss_fuzz +build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_instrumentation=oss-fuzz +build:oss-fuzz --@rules_fuzzing//fuzzing:cc_engine_sanitizer=none +build:oss-fuzz --cxxopt=-stdlib=libc++ +build:oss-fuzz --linkopt=-lc++ +build:oss-fuzz --verbose_failures +build:oss-fuzz --spawn_strategy=standalone diff --git a/MODULE.bazel b/MODULE.bazel index 82667a5b6..e9c79cd78 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -32,6 +32,10 @@ bazel_dep( version = "1.6.3", ) bazel_dep(name = "rules_license", version = "1.0.0") +bazel_dep( + name = "rules_fuzzing", + version = "0.6.0", +) bazel_dep( name = "protobuf", version = "35.1", diff --git a/fuzz/BUILD b/fuzz/BUILD new file mode 100644 index 000000000..c9c659b48 --- /dev/null +++ b/fuzz/BUILD @@ -0,0 +1,38 @@ +# 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 targets for the CEL parser and evaluator, built and run by OSS-Fuzz +# (https://github.com/google/oss-fuzz). Build the OSS-Fuzz variants with: +# +# bazel build -c opt --config=oss-fuzz //fuzz:fuzz_parse_oss_fuzz //fuzz:fuzz_eval_oss_fuzz + +load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test") + +cc_fuzz_test( + name = "fuzz_parse", + srcs = ["fuzz_parse.cc"], + deps = ["//parser"], +) + +cc_fuzz_test( + name = "fuzz_eval", + srcs = ["fuzz_eval.cc"], + deps = [ + "//eval/public:activation", + "//eval/public:builtin_func_registrar", + "//eval/public:cel_expr_builder_factory", + "//eval/public:cel_expression", + "//parser", + ], +) \ No newline at end of file diff --git a/fuzz/fuzz_eval.cc b/fuzz/fuzz_eval.cc new file mode 100644 index 000000000..a613516d1 --- /dev/null +++ b/fuzz/fuzz_eval.cc @@ -0,0 +1,55 @@ +/* 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 + +#include "parser/options.h" +#include "parser/parser.h" +#include "eval/public/cel_expression.h" +#include "eval/public/cel_expr_builder_factory.h" +#include "eval/public/builtin_func_registrar.h" +#include "eval/public/activation.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 1024) return 0; + std::string str (reinterpret_cast(data), size); + + auto builder = google::api::expr::runtime::CreateCelExpressionBuilder(); + auto status = google::api::expr::runtime::RegisterBuiltinFunctions(builder->GetRegistry()); + if (!status.ok()) { + return 0; + } + + google::api::expr::parser::ParserOptions options; + options.max_recursion_depth = 128; + options.expression_size_codepoint_limit = 1 << 20; + + try { + auto parse_status = google::api::expr::parser::Parse(str, "fuzzinput", options); + if (!parse_status.ok()) { + return 0; + } + + auto expr_status = builder->CreateExpression(&parse_status->expr(), &parse_status->source_info()); + if (expr_status.ok()) { + google::protobuf::Arena arena; + google::api::expr::runtime::Activation activation; + auto eval_status = (*expr_status)->Evaluate(activation, &arena); + } + } catch (const std::exception& e) { + return 0; + } + return 0; +} \ No newline at end of file diff --git a/fuzz/fuzz_parse.cc b/fuzz/fuzz_parse.cc new file mode 100644 index 000000000..b92d43179 --- /dev/null +++ b/fuzz/fuzz_parse.cc @@ -0,0 +1,36 @@ +// Copyright 2021 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 + +#include "parser/options.h" +#include "parser/parser.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::string str (reinterpret_cast(data), size); + google::api::expr::parser::ParserOptions options; + options.max_recursion_depth = 128; + options.expression_size_codepoint_limit = 1 << 20; + try { + auto parse_status = google::api::expr::parser::Parse(str, "fuzzinput", options); + if (!parse_status.ok()) { + static_cast(parse_status.status().message()); + } + } catch (const std::exception& e) { + return 0; + } + return 0; +} \ No newline at end of file