Oss fuzz test
diff --git a/oss-fuzz/Dockerfile b/oss-fuzz/Dockerfile
new file mode 100644
index 0000000..38fd60e
--- /dev/null
+++ b/oss-fuzz/Dockerfile
@@ -0,0 +1,29 @@
+FROM gcr.io/oss-fuzz-base/base-builder
+
+RUN apt update
+
+# Wget is needed for downloading llvm.sh (it is also used by llvm.sh)
+RUN apt install -y wget
+
+# This is needed to avoid https apt install errors
+RUN apt install -y apt-transport-https ca-certificates
+
+# This is a compiler dependency
+RUN apt install -y zlib1g-dev
+
+# We also need recent version of libstdc++
+RUN add-apt-repository ppa:ubuntu-toolchain-r/test
+RUN apt update
+RUN apt install -y gcc-9 g++-9
+
+# Install the LLVM toolchaing and libraries
+RUN wget https://apt.llvm.org/llvm.sh
+RUN chmod +x llvm.sh
+RUN ./llvm.sh 11
+RUN apt install -y llvm-11-dev libclang-11-dev liblldb-11-dev
+
+# Clone our repo
+RUN git clone https://github.com/google/lldb-eval
+
+WORKDIR lldb-eval
+COPY my_bazel_build.sh build.sh patchara.patch $SRC/
diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh
new file mode 100644
index 0000000..bbf6757
--- /dev/null
+++ b/oss-fuzz/build.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+git apply $SRC/patchara.patch
+
+export LLVM_INSTALL_PATH=/usr/lib/llvm-11/
+
+bazel build :all
+
+export BAZEL_EXTRA_BUILD_FLAGS="--@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing_oss_fuzz//:oss_fuzz_engine"
+
+#bazel_build_fuzz_tests --sandbox_debug
+
+bash $SRC/my_bazel_build.sh
diff --git a/oss-fuzz/my_bazel_build.sh b/oss-fuzz/my_bazel_build.sh
new file mode 100644
index 0000000..943c394
--- /dev/null
+++ b/oss-fuzz/my_bazel_build.sh
@@ -0,0 +1,79 @@
+#!/bin/bash -eu
+#
+# 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.
+#
+################################################################################
+
+: "${BAZEL_FUZZ_TEST_TAG:=fuzz-test}"
+: "${BAZEL_FUZZ_TEST_EXCLUDE_TAG:=no-oss-fuzz}"
+: "${BAZEL_PACKAGE_SUFFIX:=_oss_fuzz}"
+: "${BAZEL_TOOL:=bazel}"
+: "${BAZEL_EXTRA_BUILD_FLAGS:=}"
+
+if [[ -z "${BAZEL_FUZZ_TEST_QUERY:-}" ]]; then
+ BAZEL_FUZZ_TEST_QUERY="
+ let all_fuzz_tests = attr(tags, \"${BAZEL_FUZZ_TEST_TAG}\", \"//...\") in
+ \$all_fuzz_tests - attr(tags, \"${BAZEL_FUZZ_TEST_EXCLUDE_TAG}\", \$all_fuzz_tests)
+ "
+fi
+
+echo "Using Bazel query to find fuzz targets: ${BAZEL_FUZZ_TEST_QUERY}"
+
+declare -r OSS_FUZZ_TESTS=(
+ $(bazel query "${BAZEL_FUZZ_TEST_QUERY}" | sed "s/$/${BAZEL_PACKAGE_SUFFIX}/")
+)
+
+echo "Found ${#OSS_FUZZ_TESTS[@]} fuzz test packages:"
+for oss_fuzz_test in "${OSS_FUZZ_TESTS[@]}"; do
+ echo " ${oss_fuzz_test}"
+done
+
+declare -r BAZEL_BUILD_FLAGS=(
+ "--@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing_oss_fuzz//:oss_fuzz_engine" \
+ "--@rules_fuzzing//fuzzing:cc_engine_instrumentation=oss-fuzz" \
+ "--@rules_fuzzing//fuzzing:cc_engine_sanitizer=none" \
+ "--linkopt=-lc++" \
+ "--action_env=CC=${CC}" "--action_env=CXX=${CXX}" \
+ ${BAZEL_EXTRA_BUILD_FLAGS[*]}
+)
+
+echo "Building the fuzz tests with the following Bazel options:"
+echo " ${BAZEL_BUILD_FLAGS[@]}"
+
+${BAZEL_TOOL} build -s "${BAZEL_BUILD_FLAGS[@]}" "${OSS_FUZZ_TESTS[@]}"
+
+echo "Extracting the fuzz test packages in the output directory."
+for oss_fuzz_archive in $(find bazel-bin/ -name "*${BAZEL_PACKAGE_SUFFIX}.tar"); do
+ tar -xvf "${oss_fuzz_archive}" -C "${OUT}"
+done
+
+if [ "$SANITIZER" = "coverage" ]; then
+ echo "Collecting the repository source files for coverage tracking."
+ declare -r COVERAGE_SOURCES="${OUT}/proc/self/cwd"
+ mkdir -p "${COVERAGE_SOURCES}"
+ declare -r RSYNC_FILTER_ARGS=(
+ "--include" "*.h"
+ "--include" "*.cc"
+ "--include" "*.hpp"
+ "--include" "*.cpp"
+ "--include" "*.c"
+ "--include" "*.inc"
+ "--include" "*/"
+ "--exclude" "*"
+ )
+ rsync -avLk "${RSYNC_FILTER_ARGS[@]}" \
+ "$(bazel info execution_root)/" \
+ "${COVERAGE_SOURCES}/"
+fi
diff --git a/oss-fuzz/patchara.patch b/oss-fuzz/patchara.patch
new file mode 100644
index 0000000..99f48c8
--- /dev/null
+++ b/oss-fuzz/patchara.patch
@@ -0,0 +1,351 @@
+diff --git a/.bazelrc b/.bazelrc
+index d8c1d51..ed4bee4 100644
+--- a/.bazelrc
++++ b/.bazelrc
+@@ -6,7 +6,7 @@ build:linux --cxxopt=-std=c++17
+ build:linux --copt=-Wall
+ build:linux --copt=-Wextra
+ build:linux --copt=-Wpedantic
+-build:linux --copt=-Werror
++#build:linux --copt=-Werror
+ build:linux --cxxopt=-fno-exceptions
+ build:linux --cxxopt=-fno-rtti
+
+@@ -82,3 +82,15 @@ build:valgrind --copt=-DCONFIG_VALGRIND
+
+ # Try loading per-user configuration.
+ try-import %workspace%/user.bazelrc
++
++# Force the use of Clang for C++ builds
++#build:linux --action_env=CC=clang
++#build:linux --action_env=CXX=clang++
++
++# Fuzzer stuff
++build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
++build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
++build:asan-libfuzzer --@rules_fuzzing//fuzzing:cc_engine_sanitizer=asan
++
++build:libfuzzer --@rules_fuzzing//fuzzing:cc_engine=@rules_fuzzing//fuzzing/engines:libfuzzer
++build:libfuzzer --@rules_fuzzing//fuzzing:cc_engine_instrumentation=libfuzzer
+diff --git a/WORKSPACE b/WORKSPACE
+index 3ce35d4..2ce55ea 100644
+--- a/WORKSPACE
++++ b/WORKSPACE
+@@ -33,6 +33,19 @@ http_archive(
+ urls = ["https://github.com/yhirose/cpp-linenoise/archive/bc523e4b03a690cebe3b5f80a6396bcc50215a03.zip"],
+ )
+
++http_archive(
++ name = "rules_fuzzing",
++ sha256 = "71fa2724c9802c597199a86111a0499fc4fb22426d322334d3f191dadeff5132",
++ strip_prefix = "rules_fuzzing-0.1.0",
++ urls = ["https://github.com/bazelbuild/rules_fuzzing/archive/v0.1.0.zip"],
++)
++
++
++load("@rules_fuzzing//fuzzing:repositories.bzl", "rules_fuzzing_dependencies")
++rules_fuzzing_dependencies()
++load("@rules_fuzzing//fuzzing:init.bzl", "rules_fuzzing_init")
++rules_fuzzing_init()
++
+ load("//build_defs:repo_rules.bzl", "llvm_project_configure")
+
+ llvm_project_configure(name = "llvm_project")
+diff --git a/tools/fuzzer/BUILD b/tools/fuzzer/BUILD
+index d7e3885..63a3589 100644
+--- a/tools/fuzzer/BUILD
++++ b/tools/fuzzer/BUILD
+@@ -1,4 +1,5 @@
+ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
++load("@rules_fuzzing//fuzzing:cc_defs.bzl", "cc_fuzz_test")
+
+ cc_library(
+ name = "fuzzer_lib",
+@@ -106,3 +107,19 @@ cc_test(
+ "@com_google_googletest//:gtest_main",
+ ],
+ )
++
++cc_fuzz_test(
++ name = "libfuzzer",
++ data = [
++ "//testdata:fuzzer_binary_gen",
++ "//testdata:fuzzer_binary_srcs",
++ ],
++ tags = ["no-sandbox"],
++ srcs = ["libfuzzer.cc"],
++ deps = [
++ "//lldb-eval",
++ "//lldb-eval:runner",
++ "@bazel_tools//tools/cpp/runfiles",
++ ":fuzzer_lib",
++ ],
++)
+diff --git a/tools/fuzzer/expr_gen.cc b/tools/fuzzer/expr_gen.cc
+index c57cd51..33f7379 100644
+--- a/tools/fuzzer/expr_gen.cc
++++ b/tools/fuzzer/expr_gen.cc
+@@ -1320,7 +1320,7 @@ std::optional<Expr> ExprGenerator::generate() {
+
+ bool ExprGenerator::mutate_gen_node(std::shared_ptr<GenNode>& node) {
+ // Don't mutate invalid nodes.
+- if (node->is_valid()) {
++ if (!node->is_valid()) {
+ return false;
+ }
+
+@@ -1576,5 +1576,6 @@ ScalarType DefaultGeneratorRng<Rng>::gen_scalar_type(ScalarMask mask) {
+
+ template class DefaultGeneratorRng<Mt19937>;
+ template class DefaultGeneratorRng<FixedRng>;
++template class DefaultGeneratorRng<FuzzRng>;
+
+ } // namespace fuzzer
+diff --git a/tools/fuzzer/libfuzzer.cc b/tools/fuzzer/libfuzzer.cc
+new file mode 100644
+index 0000000..b812134
+--- /dev/null
++++ b/tools/fuzzer/libfuzzer.cc
+@@ -0,0 +1,160 @@
++#include <cassert>
++#include <cstdint>
++#include <memory>
++#include <sstream>
++#include <iostream>
++#include <cstdio>
++
++#include "tools/fuzzer/expr_gen.h"
++#include "tools/fuzzer/gen_node.h"
++#include "tools/fuzzer/rng.h"
++#include "tools/fuzzer/symbol_table.h"
++
++#include "lldb-eval/api.h"
++#include "lldb-eval/runner.h"
++#include "lldb/API/SBDebugger.h"
++#include "lldb/API/SBFrame.h"
++#include "lldb/API/SBProcess.h"
++#include "lldb/API/SBTarget.h"
++#include "lldb/API/SBThread.h"
++#include "tools/cpp/runfiles/runfiles.h"
++#include "tools/fuzzer/ast.h"
++
++using bazel::tools::cpp::runfiles::Runfiles;
++using std::size_t;
++using namespace fuzzer;
++
++static Runfiles* g_runfiles;
++static lldb::SBDebugger g_debugger;
++static lldb::SBProcess g_process;
++static lldb::SBFrame g_frame;
++
++static lldb::SBFrame& get_frame() {
++ static bool initialized = false;
++ if (!initialized) {
++ g_runfiles = Runfiles::CreateForTest();
++ lldb_eval::SetupLLDBServerEnv(*g_runfiles);
++ lldb::SBDebugger::Initialize();
++
++ auto binary_path =
++ g_runfiles->Rlocation("lldb_eval/testdata/fuzzer_binary");
++ auto source_path =
++ g_runfiles->Rlocation("lldb_eval/testdata/fuzzer_binary.cc");
++ g_debugger = lldb::SBDebugger::Create(false);
++ g_process = lldb_eval::LaunchTestProgram(g_debugger, source_path,
++ binary_path, "// BREAK HERE");
++ g_frame = g_process.GetSelectedThread().GetSelectedFrame();
++
++ initialized = true;
++ }
++
++ return g_frame;
++}
++
++
++SymbolTable create_symtab() {
++ fuzzer::SymbolTable symtab =
++ fuzzer::SymbolTable::create_from_frame(get_frame(), true);
++
++ return symtab;
++}
++
++
++template <class T>
++ExprGenerator create_generator(T base_rng) {
++ auto rng = std::make_unique<DefaultGeneratorRng<T>>(std::move(base_rng));
++ auto cfg = GenConfig();
++ cfg.bin_op_mask[BinOp::Shl] = false;
++ cfg.bin_op_mask[BinOp::Shr] = false;
++
++ auto symtab = create_symtab();
++ return ExprGenerator(std::move(rng), cfg, symtab);
++}
++
++// Generation tree visitor used to randomly pick nodes.
++class GenNodePicker : public GenTreeVisitor {
++ public:
++ void visit_node(std::shared_ptr<GenNode> node) {
++ if (node->is_valid()) {
++ options_.emplace_back(node);
++ }
++ }
++ template <class Rng>
++ std::shared_ptr<GenNode> pick(Rng& rng) {
++ std::uniform_int_distribution<size_t> distr(0, options_.size() - 1);
++ return options_[distr(rng)];
++ }
++ private:
++ std::vector<std::shared_ptr<GenNode>> options_;
++};
++
++template <class Rng>
++std::shared_ptr<GenNode> pick_random_node(std::shared_ptr<GenNode> root,
++ Rng& rng) {
++ GenNodePicker picker;
++ walk_gen_tree(root, &picker);
++ return picker.pick(rng);
++}
++
++class GenNodeWriter : public GenTreeVisitor {
++ public:
++ explicit GenNodeWriter(FuzzWriter& writer) : writer_(writer) {}
++
++ void visit_random_value(rand_t value) {
++ writer_.write(value);
++ }
++
++ private:
++ FuzzWriter& writer_;
++};
++
++void write_node(std::shared_ptr<GenNode> root, FuzzWriter& writer) {
++ GenNodeWriter node_writer(writer);
++ walk_gen_tree(root, &node_writer);
++}
++
++extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data, size_t size,
++ size_t max_size, unsigned int seed) {
++ FuzzReader reader(data, size);
++ auto fixed_generator = create_generator(FuzzRng(std::move(reader)));
++ assert(fixed_generator.generate() && "Expression couldn't be generated!");
++
++ std::mt19937 rng(seed);
++ auto root = fixed_generator.node();
++ auto mutable_node = pick_random_node(root, rng);
++
++ auto random_generator = create_generator(Mt19937(rng()));
++ if (!random_generator.mutate_gen_node(mutable_node)) {
++ return size;
++ }
++
++
++ FuzzWriter writer(data, max_size);
++ write_node(root, writer);
++
++ return writer.size();
++}
++
++extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
++ static int counter = 0;
++ counter++;
++ if (counter == 20) {
++ // assert(false);
++ }
++
++ FuzzReader reader(data, size);
++ auto generator = create_generator(FuzzRng(std::move(reader)));
++ auto maybe_expr = generator.generate();
++
++ if (maybe_expr) {
++ std::ostringstream os;
++ os << *maybe_expr;
++ //printf("expr: `%s`\n", os.str().c_str());
++
++ lldb::SBError error;
++ lldb::SBValue value = lldb_eval::EvaluateExpression(get_frame(), os.str().c_str(), error);
++ (void)value;
++ }
++
++ return 0;
++}
+diff --git a/tools/fuzzer/rng.h b/tools/fuzzer/rng.h
+index a59be7e..675c17a 100644
+--- a/tools/fuzzer/rng.h
++++ b/tools/fuzzer/rng.h
+@@ -77,6 +77,77 @@ class FixedRng : public RngCallbackNotifier {
+ std::queue<rand_t> queue_;
+ };
+
++
++class FuzzReader {
++ public:
++ FuzzReader(const uint8_t* data, size_t size)
++ : data_(data), size_(size), cursor_(0) {}
++
++ rand_t read() {
++ rand_t result = next_byte();
++ result <<= 8;
++ result |= next_byte();
++ result <<= 8;
++ result |= next_byte();
++ result <<= 8;
++ result |= next_byte();
++ return result;
++ }
++
++ private:
++ uint8_t next_byte() {
++ return cursor_ < size_ ? data_[cursor_++] : 0;
++ }
++
++ private:
++ const uint8_t* data_;
++ size_t size_;
++ size_t cursor_;
++};
++
++class FuzzWriter {
++ public:
++ FuzzWriter(uint8_t* data, size_t max_size)
++ : data_(data), max_size_(max_size), size_(0) {}
++
++ void write(unsigned int value) {
++ write_byte((value >> 24) & 0xff);
++ write_byte((value >> 16) & 0xff);
++ write_byte((value >> 8) & 0xff);
++ write_byte(value & 0xff);
++ }
++
++ size_t size() const { return size_; }
++
++ private:
++ void write_byte(uint8_t byte) {
++ if (size_ < max_size_) {
++ data_[size_++] = byte;
++ }
++ }
++
++ private:
++ uint8_t* data_;
++ size_t max_size_;
++ size_t size_;
++};
++
++class FuzzRng : public RngCallbackNotifier {
++ public:
++ explicit FuzzRng(FuzzReader reader)
++ : reader_(std::move(reader)) {}
++
++ using result_type = rand_t;
++ static constexpr result_type min() { return std::mt19937::min(); }
++ static constexpr result_type max() { return std::mt19937::max(); }
++
++ result_type operator()() { return consume(reader_.read()); }
++
++ private:
++ FuzzReader reader_;
++};
++
++
+ } // namespace fuzzer
+
+ #endif // INCLUDE_RNG_H
diff --git a/oss-fuzz/project.yaml b/oss-fuzz/project.yaml
new file mode 100644
index 0000000..9a1ad4c
--- /dev/null
+++ b/oss-fuzz/project.yaml
@@ -0,0 +1,4 @@
+homepage: example.com
+language: c++
+santizers:
+ - memory