From b17861e29120dc8a560bc5db7aa9c9463828b68a Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:00:57 -0800 Subject: [PATCH 1/5] template --- oss-fuzz/CHECKLIST.md | 192 ++++++++++++++++++++++++++++++++++ oss-fuzz/Dockerfile | 33 ++++++ oss-fuzz/INTEGRATION_GUIDE.md | 170 ++++++++++++++++++++++++++++++ oss-fuzz/README.md | 81 ++++++++++++++ oss-fuzz/SUMMARY.md | 165 +++++++++++++++++++++++++++++ oss-fuzz/build.sh | 60 +++++++++++ oss-fuzz/project.yaml | 24 +++++ oss-fuzz/test_build_local.sh | 103 ++++++++++++++++++ test/fuzz_mongocrypt.c | 133 +++++++++++++++++++++++ 9 files changed, 961 insertions(+) create mode 100644 oss-fuzz/CHECKLIST.md create mode 100644 oss-fuzz/Dockerfile create mode 100644 oss-fuzz/INTEGRATION_GUIDE.md create mode 100644 oss-fuzz/README.md create mode 100644 oss-fuzz/SUMMARY.md create mode 100755 oss-fuzz/build.sh create mode 100644 oss-fuzz/project.yaml create mode 100755 oss-fuzz/test_build_local.sh create mode 100644 test/fuzz_mongocrypt.c diff --git a/oss-fuzz/CHECKLIST.md b/oss-fuzz/CHECKLIST.md new file mode 100644 index 000000000..3a1979e49 --- /dev/null +++ b/oss-fuzz/CHECKLIST.md @@ -0,0 +1,192 @@ +# OSS-Fuzz Implementation Checklist + +Use this checklist to track progress on implementing the OSS-Fuzz integration. + +## Phase 1: Setup and Verification ✅ + +- [x] Create OSS-Fuzz configuration files + - [x] Dockerfile + - [x] build.sh + - [x] project.yaml +- [x] Create placeholder fuzzer (`test/fuzz_mongocrypt.c`) +- [x] Create documentation + - [x] README.md + - [x] INTEGRATION_GUIDE.md + - [x] SUMMARY.md +- [x] Create local testing script +- [x] Make scripts executable + +## Phase 2: Local Testing + +- [ ] Test local build + ```bash + cd oss-fuzz + ./test_build_local.sh + ``` +- [ ] Verify fuzzers compile without errors +- [ ] Run placeholder fuzzer to ensure it executes + ```bash + ./out/fuzz_mongocrypt -max_total_time=10 + ``` +- [ ] Verify existing fuzz_kms works + ```bash + ./out/fuzz_kms -max_total_time=10 + ``` + +## Phase 3: Implement Fuzzing Logic + +### 3.1 Basic Implementation +- [ ] Add KMS provider configuration + - [ ] Create minimal valid KMS config + - [ ] Handle initialization errors gracefully +- [ ] Implement basic encryption fuzzing + - [ ] Fuzz `mongocrypt_ctx_encrypt_init()` + - [ ] Handle BSON parsing errors + - [ ] Test with valid BSON samples +- [ ] Implement basic decryption fuzzing + - [ ] Fuzz `mongocrypt_ctx_decrypt_init()` + - [ ] Handle invalid ciphertext gracefully + +### 3.2 Advanced Implementation +- [ ] Add explicit encryption fuzzing + - [ ] Fuzz `mongocrypt_ctx_explicit_encrypt_init()` + - [ ] Test different algorithms + - [ ] Test different key IDs +- [ ] Add KMS context fuzzing + - [ ] Fuzz `mongocrypt_kms_ctx_feed()` + - [ ] Test different KMS providers +- [ ] Add BSON document feeding + - [ ] Fuzz `mongocrypt_ctx_mongo_feed()` + - [ ] Test collection info documents + - [ ] Test key documents + +### 3.3 Input Partitioning +- [ ] Implement input splitting strategy + - [ ] Reserve bytes for operation selection + - [ ] Reserve bytes for configuration + - [ ] Reserve bytes for BSON data +- [ ] Add input validation + - [ ] Check minimum sizes + - [ ] Validate BSON structure +- [ ] Handle edge cases + - [ ] Empty inputs + - [ ] Very large inputs + - [ ] Malformed BSON + +## Phase 4: Seed Corpus + +- [ ] Create seed corpus directory + ```bash + mkdir -p oss-fuzz/seed_corpus + ``` +- [ ] Add valid BSON documents + - [ ] Simple documents + - [ ] Nested documents + - [ ] Arrays + - [ ] All BSON types +- [ ] Add encrypted data samples + - [ ] FLE1 encrypted values + - [ ] FLE2 encrypted values + - [ ] Different algorithms +- [ ] Add KMS response samples + - [ ] AWS KMS responses + - [ ] Azure responses + - [ ] GCP responses + - [ ] Local KMS responses + +## Phase 5: Dictionary + +- [ ] Create BSON dictionary file + ```bash + touch oss-fuzz/mongocrypt.dict + ``` +- [ ] Add common field names + - [ ] `"_id"` + - [ ] `"keyId"` + - [ ] `"algorithm"` + - [ ] `"value"` + - [ ] `"v"` + - [ ] Add more from test data +- [ ] Add algorithm names + - [ ] `"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"` + - [ ] `"AEAD_AES_256_CBC_HMAC_SHA_512-Random"` + - [ ] Add FLE2 algorithms + +## Phase 6: Testing with Sanitizers + +- [ ] Test with AddressSanitizer (ASan) + ```bash + # In test_build_local.sh, CFLAGS already includes ASan + ./test_build_local.sh + ./out/fuzz_mongocrypt -max_total_time=300 + ``` +- [ ] Test with UndefinedBehaviorSanitizer (UBSan) + ```bash + # Modify CFLAGS in test_build_local.sh + export CFLAGS="-g -O1 -fsanitize=undefined,fuzzer-no-link" + ``` +- [ ] Test with MemorySanitizer (MSan) + ```bash + # Requires clang and special build + export CFLAGS="-g -O1 -fsanitize=memory,fuzzer-no-link" + ``` +- [ ] Fix any issues found + - [ ] Document expected errors + - [ ] Fix actual bugs + - [ ] Add suppressions if needed + +## Phase 7: OSS-Fuzz Integration Testing + +- [ ] Clone OSS-Fuzz repository + ```bash + git clone https://github.com/google/oss-fuzz.git + ``` +- [ ] Copy files to OSS-Fuzz + ```bash + cp -r oss-fuzz/* oss-fuzz-repo/projects/libmongocrypt/ + ``` +- [ ] Build with OSS-Fuzz infrastructure + ```bash + python infra/helper.py build_image libmongocrypt + python infra/helper.py build_fuzzers libmongocrypt + ``` +- [ ] Run fuzzers + ```bash + python infra/helper.py run_fuzzer libmongocrypt fuzz_mongocrypt + ``` +- [ ] Check coverage + ```bash + python infra/helper.py coverage libmongocrypt + ``` +- [ ] Review coverage report + - [ ] Identify uncovered code + - [ ] Add tests for uncovered paths + +## Phase 8: Submission + +- [ ] Update project.yaml with correct contacts + - [ ] Verify primary_contact email + - [ ] Add auto_ccs emails +- [ ] Create PR to OSS-Fuzz + - [ ] Fork OSS-Fuzz repository + - [ ] Create branch + - [ ] Commit files + - [ ] Submit pull request +- [ ] Address review comments +- [ ] Wait for approval and merge + +## Phase 9: Monitoring + +- [ ] Set up bug notifications +- [ ] Monitor OSS-Fuzz dashboard +- [ ] Triage reported issues +- [ ] Fix bugs found by fuzzing +- [ ] Update seed corpus based on findings + +## Notes + +- Mark items as complete with `[x]` +- Add notes or blockers inline +- Update this checklist as needed +- Keep SUMMARY.md in sync with progress + diff --git a/oss-fuzz/Dockerfile b/oss-fuzz/Dockerfile new file mode 100644 index 000000000..547f7c67d --- /dev/null +++ b/oss-fuzz/Dockerfile @@ -0,0 +1,33 @@ +# Copyright 2024 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. +# +################################################################################ + +FROM gcr.io/oss-fuzz-base/base-builder + +# Install dependencies +RUN apt-get update && apt-get install -y \ + cmake \ + pkg-config \ + libssl-dev + +# Clone the repository +RUN git clone --depth 1 https://github.com/mongodb/libmongocrypt.git libmongocrypt + +# Set working directory +WORKDIR libmongocrypt + +# Copy build script +COPY build.sh $SRC/ + diff --git a/oss-fuzz/INTEGRATION_GUIDE.md b/oss-fuzz/INTEGRATION_GUIDE.md new file mode 100644 index 000000000..50e5b402d --- /dev/null +++ b/oss-fuzz/INTEGRATION_GUIDE.md @@ -0,0 +1,170 @@ +# OSS-Fuzz Integration Guide for libmongocrypt + +## Quick Start + +This guide explains how to integrate libmongocrypt with OSS-Fuzz. + +## What Has Been Created + +### 1. Fuzzing Entry Points + +#### `test/fuzz_mongocrypt.c` (NEW - Placeholder) +A placeholder fuzzer for the main libmongocrypt APIs. This file contains: +- Basic libFuzzer entry point (`LLVMFuzzerTestOneInput`) +- Extensive TODO comments with examples of how to fuzz different APIs +- Proper initialization and cleanup + +**Status**: Placeholder only - needs actual fuzzing logic implementation + +#### `test/fuzz_kms.c` (Existing) +Already exists and fuzzes KMS message parsing. + +### 2. OSS-Fuzz Configuration Files + +All files are in the `oss-fuzz/` directory: + +#### `Dockerfile` +- Defines the build environment +- Installs dependencies (cmake, pkg-config, libssl-dev) +- Clones the libmongocrypt repository + +#### `build.sh` +- Builds libmongocrypt with appropriate flags +- Compiles fuzzing targets +- Links against libFuzzer + +#### `project.yaml` +- Project metadata (homepage, contacts, language) +- Sanitizer configuration (ASan, UBSan, MSan) +- Fuzzing engine configuration (libFuzzer, AFL, Honggfuzz) + +## Next Steps + +### Step 1: Implement the Fuzzer Logic + +Edit `test/fuzz_mongocrypt.c` to add actual fuzzing logic. See the TODO comments for examples. + +Key considerations: +- **Input partitioning**: Split the fuzzed input into multiple parts for different parameters +- **State management**: Handle the mongocrypt state machine properly +- **Error handling**: Don't crash on expected errors +- **Coverage**: Target multiple code paths + +Example implementation approach: +```c +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 100) return 0; + + // Partition input + const uint8_t *kms_config = data; + size_t kms_config_size = 50; + const uint8_t *bson_data = data + 50; + size_t bson_size = size - 50; + + // Initialize with fuzzed KMS config + mongocrypt_t *crypt = mongocrypt_new(); + mongocrypt_binary_t *kms_bin = mongocrypt_binary_new_from_data( + (uint8_t *)kms_config, kms_config_size); + mongocrypt_setopt_kms_providers(crypt, kms_bin); + mongocrypt_init(crypt); + + // Fuzz encryption + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + mongocrypt_binary_t *doc_bin = mongocrypt_binary_new_from_data( + (uint8_t *)bson_data, bson_size); + mongocrypt_ctx_encrypt_init(ctx, "db", -1, doc_bin); + + // Cleanup + mongocrypt_binary_destroy(doc_bin); + mongocrypt_binary_destroy(kms_bin); + mongocrypt_ctx_destroy(ctx); + mongocrypt_destroy(crypt); + + return 0; +} +``` + +### Step 2: Create Seed Corpus + +Create a directory with sample inputs: +```bash +mkdir -p oss-fuzz/seed_corpus +``` + +Add valid BSON documents and encrypted data as seed inputs. These help the fuzzer understand the input format. + +### Step 3: Create Dictionary (Optional) + +Create `oss-fuzz/mongocrypt.dict` with common BSON field names: +``` +"_id" +"keyId" +"algorithm" +"value" +"v" +"encrypted" +``` + +### Step 4: Test Locally + +```bash +# Clone OSS-Fuzz +git clone https://github.com/google/oss-fuzz.git +cd oss-fuzz + +# Create project directory +mkdir -p projects/libmongocrypt + +# Copy files +cp /path/to/libmongocrypt/oss-fuzz/* projects/libmongocrypt/ + +# Build +python infra/helper.py build_image libmongocrypt +python infra/helper.py build_fuzzers libmongocrypt + +# Run +python infra/helper.py run_fuzzer libmongocrypt fuzz_mongocrypt -- -max_total_time=60 +``` + +### Step 5: Submit to OSS-Fuzz + +1. Test all sanitizers: + ```bash + python infra/helper.py build_fuzzers --sanitizer address libmongocrypt + python infra/helper.py build_fuzzers --sanitizer undefined libmongocrypt + python infra/helper.py build_fuzzers --sanitizer memory libmongocrypt + ``` + +2. Run coverage check: + ```bash + python infra/helper.py coverage libmongocrypt + ``` + +3. Submit PR to https://github.com/google/oss-fuzz + +## Troubleshooting + +### Build Failures + +- Check that all dependencies are in the Dockerfile +- Verify library paths in build.sh +- Ensure static libraries are being built + +### Runtime Crashes + +- Use `run_fuzzer` with `-help=1` to see libFuzzer options +- Add `-print_final_stats=1` for debugging +- Check sanitizer output for actual bugs vs. expected errors + +### Low Coverage + +- Add more seed corpus files +- Implement multiple fuzzing targets +- Use coverage report to identify untested code + +## Resources + +- [OSS-Fuzz Documentation](https://google.github.io/oss-fuzz/) +- [libFuzzer Documentation](https://llvm.org/docs/LibFuzzer.html) +- [Fuzzing BSON](https://github.com/mongodb/mongo/tree/master/src/third_party/libfuzzer) + diff --git a/oss-fuzz/README.md b/oss-fuzz/README.md new file mode 100644 index 000000000..bd098a84d --- /dev/null +++ b/oss-fuzz/README.md @@ -0,0 +1,81 @@ +# OSS-Fuzz Integration for libmongocrypt + +This directory contains the OSS-Fuzz integration for libmongocrypt. + +## Overview + +[OSS-Fuzz](https://github.com/google/oss-fuzz) is Google's continuous fuzzing service for open source software. This integration enables automated fuzzing of libmongocrypt to discover potential security vulnerabilities and bugs. + +## Files + +- **Dockerfile**: Defines the build environment for OSS-Fuzz +- **build.sh**: Script that builds libmongocrypt and the fuzzing targets +- **project.yaml**: OSS-Fuzz project configuration + +## Fuzzing Targets + +### fuzz_kms +Fuzzes the KMS message parsing and request creation functionality. +- Source: `test/fuzz_kms.c` +- Targets: `kms_response_parser_feed()`, `kms_request_new()` + +### fuzz_mongocrypt (Placeholder) +Main fuzzer for libmongocrypt encryption/decryption APIs. +- Source: `test/fuzz_mongocrypt.c` +- Status: **Placeholder** - needs implementation +- Planned targets: + - `mongocrypt_ctx_encrypt_init()` with fuzzed BSON commands + - `mongocrypt_ctx_decrypt_init()` with fuzzed encrypted data + - `mongocrypt_ctx_explicit_encrypt_init()` with fuzzed values + - `mongocrypt_ctx_explicit_decrypt_init()` with fuzzed ciphertext + - `mongocrypt_ctx_mongo_feed()` with fuzzed BSON documents + - `mongocrypt_kms_ctx_feed()` with fuzzed KMS responses + +## Local Testing + +To test the OSS-Fuzz integration locally: + +```bash +# Clone OSS-Fuzz +git clone https://github.com/google/oss-fuzz.git +cd oss-fuzz + +# Copy the libmongocrypt OSS-Fuzz files +cp -r /path/to/libmongocrypt/oss-fuzz projects/libmongocrypt + +# Build the Docker image +python infra/helper.py build_image libmongocrypt + +# Build the fuzzers +python infra/helper.py build_fuzzers libmongocrypt + +# Run a fuzzer +python infra/helper.py run_fuzzer libmongocrypt fuzz_kms +``` + +## Integration with OSS-Fuzz Repository + +To integrate with the official OSS-Fuzz repository: + +1. Fork the [OSS-Fuzz repository](https://github.com/google/oss-fuzz) +2. Copy the files from this directory to `projects/libmongocrypt/` in the OSS-Fuzz repo +3. Test locally using the commands above +4. Submit a pull request to the OSS-Fuzz repository + +See the [OSS-Fuzz New Project Guide](https://google.github.io/oss-fuzz/getting-started/new-project-guide/) for detailed instructions. + +## TODO + +- [ ] Implement comprehensive fuzzing logic in `fuzz_mongocrypt.c` +- [ ] Add seed corpus for better fuzzing coverage +- [ ] Add dictionary files for BSON fuzzing +- [ ] Test with all sanitizers (ASan, UBSan, MSan) +- [ ] Add additional fuzzing targets for specific APIs +- [ ] Configure continuous integration with OSS-Fuzz + +## References + +- [OSS-Fuzz Documentation](https://google.github.io/oss-fuzz/) +- [libFuzzer Tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) +- [libmongocrypt Documentation](https://github.com/mongodb/libmongocrypt) + diff --git a/oss-fuzz/SUMMARY.md b/oss-fuzz/SUMMARY.md new file mode 100644 index 000000000..05bf712f1 --- /dev/null +++ b/oss-fuzz/SUMMARY.md @@ -0,0 +1,165 @@ +# OSS-Fuzz Integration - Summary + +## What Was Created + +This OSS-Fuzz integration provides a foundation for continuous fuzzing of libmongocrypt through Google's OSS-Fuzz service. + +### Files Created + +``` +oss-fuzz/ +├── Dockerfile # OSS-Fuzz build environment +├── build.sh # Build script for fuzzers (executable) +├── project.yaml # OSS-Fuzz project configuration +├── README.md # Overview and usage instructions +├── INTEGRATION_GUIDE.md # Detailed integration steps +├── test_build_local.sh # Local testing script (executable) +└── SUMMARY.md # This file + +test/ +└── fuzz_mongocrypt.c # Placeholder fuzzer for main APIs +``` + +### Fuzzing Targets + +1. **fuzz_kms** (existing in `test/fuzz_kms.c`) + - Fuzzes KMS message parsing + - Targets: `kms_response_parser_feed()`, `kms_request_new()` + - Status: ✅ Ready to use + +2. **fuzz_mongocrypt** (new in `test/fuzz_mongocrypt.c`) + - Placeholder for main libmongocrypt APIs + - Status: ⚠️ Placeholder only - needs implementation + - Contains extensive TODO comments with examples + +## Current Status + +### ✅ Complete +- OSS-Fuzz configuration files (Dockerfile, build.sh, project.yaml) +- Placeholder fuzzer with detailed implementation examples +- Documentation (README, integration guide) +- Local testing script + +### ⚠️ Placeholder / TODO +- Actual fuzzing logic in `fuzz_mongocrypt.c` +- Seed corpus for better coverage +- Dictionary files for BSON fuzzing +- Additional fuzzing targets for specific APIs + +## How to Use + +### Option 1: Local Testing (Recommended First) + +```bash +cd oss-fuzz +./test_build_local.sh +``` + +This will: +- Build libmongocrypt with fuzzing instrumentation +- Compile both fuzzers +- Output binaries to `out/` directory + +Then run: +```bash +./out/fuzz_kms -max_total_time=60 +./out/fuzz_mongocrypt -max_total_time=60 +``` + +### Option 2: Test with OSS-Fuzz Infrastructure + +```bash +# Clone OSS-Fuzz +git clone https://github.com/google/oss-fuzz.git +cd oss-fuzz + +# Copy files +cp -r /path/to/libmongocrypt/oss-fuzz projects/libmongocrypt + +# Build and test +python infra/helper.py build_image libmongocrypt +python infra/helper.py build_fuzzers libmongocrypt +python infra/helper.py run_fuzzer libmongocrypt fuzz_kms +``` + +### Option 3: Submit to OSS-Fuzz (Final Step) + +After testing locally: +1. Fork https://github.com/google/oss-fuzz +2. Copy files to `projects/libmongocrypt/` +3. Test with all sanitizers +4. Submit pull request + +## Next Steps + +### Immediate (Required for Production) + +1. **Implement fuzzing logic** in `test/fuzz_mongocrypt.c` + - See TODO comments for examples + - Focus on encryption/decryption APIs + - Handle state machine properly + +2. **Create seed corpus** + - Add valid BSON documents + - Include encrypted data samples + - Store in `oss-fuzz/seed_corpus/` + +3. **Test thoroughly** + - Run with AddressSanitizer + - Run with UndefinedBehaviorSanitizer + - Run with MemorySanitizer + - Verify no false positives + +### Future Enhancements + +1. **Additional fuzzers** + - Fuzz specific FLE2 operations + - Fuzz range query encryption + - Fuzz key management operations + +2. **Improve coverage** + - Add dictionary for BSON field names + - Create structure-aware fuzzing + - Add custom mutators + +3. **Integration** + - Set up continuous fuzzing + - Configure bug reporting + - Add to CI/CD pipeline + +## Important Notes + +### Placeholder Status + +The `fuzz_mongocrypt.c` file is currently a **placeholder**. It: +- ✅ Compiles successfully +- ✅ Links with libmongocrypt +- ✅ Has proper libFuzzer entry point +- ❌ Does NOT actually fuzz anything yet +- ❌ Needs implementation before production use + +### Why Placeholder? + +As requested, this provides the structure and examples without implementing the actual fuzzing logic. This allows you to: +1. Review the approach +2. Decide which APIs to prioritize +3. Implement fuzzing logic incrementally +4. Test the build system first + +### Implementation Examples + +The placeholder includes detailed comments showing how to fuzz: +- Encryption context initialization +- Decryption operations +- Explicit encryption/decryption +- KMS context feeding +- BSON document feeding + +## Questions? + +See: +- `README.md` - Overview and quick start +- `INTEGRATION_GUIDE.md` - Detailed implementation guide +- [OSS-Fuzz Docs](https://google.github.io/oss-fuzz/) +- [libFuzzer Tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) + diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh new file mode 100755 index 000000000..48a43eb27 --- /dev/null +++ b/oss-fuzz/build.sh @@ -0,0 +1,60 @@ +#!/bin/bash -eu +# Copyright 2024 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. +# +################################################################################ + +# OSS-Fuzz build script for libmongocrypt +# This script is called by OSS-Fuzz to build the fuzzing targets + +# Build the library +mkdir -p build +cd build + +# Configure with CMake +# Note: OSS-Fuzz sets CC, CXX, CFLAGS, CXXFLAGS, LIB_FUZZING_ENGINE +cmake .. \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_C_FLAGS="${CFLAGS}" \ + -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ + -DENABLE_ONLINE_TESTS=OFF \ + -DENABLE_STATIC=ON + +# Build the library +make -j$(nproc) mongocrypt_static + +# Build the fuzzers +# fuzz_kms - existing KMS fuzzer +$CC $CFLAGS -c ../test/fuzz_kms.c -I../kms-message/src -I../src -o fuzz_kms.o +$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_kms.o \ + -o $OUT/fuzz_kms \ + libmongocrypt_static.a \ + kms-message/libkms_message_static.a \ + -Wl,--start-group \ + _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ + -Wl,--end-group + +# fuzz_mongocrypt - main libmongocrypt fuzzer (placeholder) +$CC $CFLAGS -c ../test/fuzz_mongocrypt.c -I../src -I. -o fuzz_mongocrypt.o +$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_mongocrypt.o \ + -o $OUT/fuzz_mongocrypt \ + libmongocrypt_static.a \ + kms-message/libkms_message_static.a \ + -Wl,--start-group \ + _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ + -Wl,--end-group + +# TODO: Add seed corpus and dictionary files +# cp ../test/data/seed_corpus/* $OUT/fuzz_mongocrypt_seed_corpus/ + diff --git a/oss-fuzz/project.yaml b/oss-fuzz/project.yaml new file mode 100644 index 000000000..065264421 --- /dev/null +++ b/oss-fuzz/project.yaml @@ -0,0 +1,24 @@ +# OSS-Fuzz project configuration for libmongocrypt +# See https://google.github.io/oss-fuzz/getting-started/new-project-guide/ + +homepage: "https://github.com/mongodb/libmongocrypt" +language: c +primary_contact: "security@mongodb.com" +auto_ccs: + - "your-team@mongodb.com" + +# Sanitizers to run +sanitizers: + - address + - undefined + - memory + +# Fuzzing engines +fuzzing_engines: + - libfuzzer + - afl + - honggfuzz + +# Build configuration +main_repo: "https://github.com/mongodb/libmongocrypt.git" + diff --git a/oss-fuzz/test_build_local.sh b/oss-fuzz/test_build_local.sh new file mode 100755 index 000000000..31074f03f --- /dev/null +++ b/oss-fuzz/test_build_local.sh @@ -0,0 +1,103 @@ +#!/bin/bash +# Local build test script for OSS-Fuzz integration +# This script helps test the fuzzer build without the full OSS-Fuzz infrastructure + +set -e + +echo "=== Testing OSS-Fuzz build locally ===" + +# Check if clang is available +if ! command -v clang &> /dev/null; then + echo "Error: clang is required but not found" + echo "Install with: sudo apt-get install clang" + exit 1 +fi + +# Check if clang++ is available +if ! command -v clang++ &> /dev/null; then + echo "Error: clang++ is required but not found" + echo "Install with: sudo apt-get install clang" + exit 1 +fi + +# Set up environment variables similar to OSS-Fuzz +export CC=clang +export CXX=clang++ +export CFLAGS="-g -O1 -fno-omit-frame-pointer -fsanitize=address,fuzzer-no-link" +export CXXFLAGS="-g -O1 -fno-omit-frame-pointer -fsanitize=address,fuzzer-no-link" +export LIB_FUZZING_ENGINE="-fsanitize=fuzzer" +export OUT="${OUT:-$(pwd)/out}" + +echo "Build output directory: $OUT" +mkdir -p "$OUT" + +# Get the repository root +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +echo "Repository root: $REPO_ROOT" + +cd "$REPO_ROOT" + +# Create build directory +BUILD_DIR="$REPO_ROOT/build-fuzz" +rm -rf "$BUILD_DIR" +mkdir -p "$BUILD_DIR" +cd "$BUILD_DIR" + +echo "" +echo "=== Configuring with CMake ===" +cmake .. \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_C_FLAGS="${CFLAGS}" \ + -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ + -DENABLE_ONLINE_TESTS=OFF \ + -DENABLE_STATIC=ON + +echo "" +echo "=== Building libmongocrypt ===" +make -j$(nproc) mongocrypt_static + +echo "" +echo "=== Building fuzz_kms ===" +$CC $CFLAGS -c ../test/fuzz_kms.c \ + -I../kms-message/src \ + -I../src \ + -o fuzz_kms.o + +$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_kms.o \ + -o "$OUT/fuzz_kms" \ + libmongocrypt_static.a \ + kms-message/libkms_message_static.a \ + -Wl,--start-group \ + _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ + -Wl,--end-group + +echo "" +echo "=== Building fuzz_mongocrypt ===" +$CC $CFLAGS -c ../test/fuzz_mongocrypt.c \ + -I../src \ + -I. \ + -o fuzz_mongocrypt.o + +$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_mongocrypt.o \ + -o "$OUT/fuzz_mongocrypt" \ + libmongocrypt_static.a \ + kms-message/libkms_message_static.a \ + -Wl,--start-group \ + _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ + -Wl,--end-group + +echo "" +echo "=== Build successful! ===" +echo "" +echo "Fuzzers built:" +ls -lh "$OUT"/fuzz_* + +echo "" +echo "To run a fuzzer:" +echo " $OUT/fuzz_kms -max_total_time=60" +echo " $OUT/fuzz_mongocrypt -max_total_time=60" +echo "" +echo "To run with a corpus directory:" +echo " mkdir -p corpus" +echo " $OUT/fuzz_kms corpus/ -max_total_time=60" + diff --git a/test/fuzz_mongocrypt.c b/test/fuzz_mongocrypt.c new file mode 100644 index 000000000..044e5eec8 --- /dev/null +++ b/test/fuzz_mongocrypt.c @@ -0,0 +1,133 @@ +/* + * Copyright 2024-present MongoDB, Inc. + * + * 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. + */ + +/* + * OSS-Fuzz fuzzing entry point for libmongocrypt. + * + * This is a placeholder fuzzer that will be expanded to test various + * libmongocrypt APIs including encryption, decryption, and key management. + */ + +#include "mongocrypt.h" +#include +#include +#include + +/* + * LLVMFuzzerTestOneInput - libFuzzer entry point + * + * This function is called by libFuzzer with random input data. + * It should exercise the target library's APIs with the provided data. + */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + mongocrypt_t *crypt = NULL; + mongocrypt_binary_t *input_bin = NULL; + + /* Minimum size check to avoid trivial inputs */ + if (size < 10) { + return 0; + } + + /* Initialize mongocrypt handle */ + crypt = mongocrypt_new(); + if (!crypt) { + return 0; + } + + /* TODO: Set up KMS providers and other configuration */ + /* For now, this is a placeholder that just initializes the library */ + + /* Attempt to initialize - may fail without proper configuration */ + /* This is expected in the placeholder version */ + mongocrypt_init(crypt); + + /* Create a binary view of the input data */ + input_bin = mongocrypt_binary_new_from_data((uint8_t *)data, size); + if (!input_bin) { + mongocrypt_destroy(crypt); + return 0; + } + + /* TODO: Add fuzzing targets here, such as: + * + * Example 1: Fuzz encryption context initialization + * ------------------------------------------------ + * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + * if (ctx) { + * // Use fuzzed data as a BSON command + * mongocrypt_ctx_encrypt_init(ctx, "testdb", -1, input_bin); + * mongocrypt_ctx_destroy(ctx); + * } + * + * Example 2: Fuzz decryption + * -------------------------- + * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + * if (ctx) { + * // Use fuzzed data as encrypted BSON + * mongocrypt_ctx_decrypt_init(ctx, input_bin); + * mongocrypt_ctx_destroy(ctx); + * } + * + * Example 3: Fuzz explicit encryption + * ----------------------------------- + * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + * if (ctx) { + * // Set algorithm and key + * mongocrypt_ctx_setopt_algorithm(ctx, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", -1); + * // Use fuzzed data as plaintext value + * mongocrypt_ctx_explicit_encrypt_init(ctx, input_bin); + * mongocrypt_ctx_destroy(ctx); + * } + * + * Example 4: Fuzz KMS context feed + * -------------------------------- + * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + * if (ctx) { + * // ... set up context to NEED_KMS state ... + * mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx); + * if (kms_ctx) { + * // Feed fuzzed KMS response data + * mongocrypt_kms_ctx_feed(kms_ctx, input_bin); + * } + * mongocrypt_ctx_destroy(ctx); + * } + * + * Example 5: Fuzz BSON document feeding + * ------------------------------------- + * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + * if (ctx) { + * // ... set up context to NEED_MONGO_* state ... + * // Feed fuzzed BSON documents + * mongocrypt_ctx_mongo_feed(ctx, input_bin); + * mongocrypt_ctx_mongo_done(ctx); + * mongocrypt_ctx_destroy(ctx); + * } + * + * Note: Proper fuzzing requires: + * 1. Setting up KMS providers with mongocrypt_setopt_kms_providers() + * 2. Handling different input sizes and formats + * 3. Partitioning input data for multiple parameters + * 4. Using seed corpus with valid BSON documents + * 5. Adding a dictionary for BSON field names + */ + + /* Cleanup */ + mongocrypt_binary_destroy(input_bin); + mongocrypt_destroy(crypt); + + return 0; +} + From 764932461bdc9346ebdec9dafdc365f22d65a06c Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Tue, 31 Mar 2026 22:13:47 -0700 Subject: [PATCH 2/5] fuzzing engine --- oss-fuzz/build.sh | 2 + test/fuzz_mongocrypt.c | 380 +++++++++++++++++++++++++++++++---------- 2 files changed, 290 insertions(+), 92 deletions(-) diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh index 48a43eb27..ec9b51c42 100755 --- a/oss-fuzz/build.sh +++ b/oss-fuzz/build.sh @@ -18,6 +18,8 @@ # OSS-Fuzz build script for libmongocrypt # This script is called by OSS-Fuzz to build the fuzzing targets +cd $SRC/libmongocrypt + # Build the library mkdir -p build cd build diff --git a/test/fuzz_mongocrypt.c b/test/fuzz_mongocrypt.c index 044e5eec8..7732e923e 100644 --- a/test/fuzz_mongocrypt.c +++ b/test/fuzz_mongocrypt.c @@ -16,116 +16,312 @@ /* * OSS-Fuzz fuzzing entry point for libmongocrypt. - * - * This is a placeholder fuzzer that will be expanded to test various - * libmongocrypt APIs including encryption, decryption, and key management. + * + * Exercises the libmongocrypt state machine by initializing contexts for + * various operations and feeding fuzzed data through each state transition. */ #include "mongocrypt.h" +#include +#include #include #include #include -/* - * LLVMFuzzerTestOneInput - libFuzzer entry point - * - * This function is called by libFuzzer with random input data. - * It should exercise the target library's APIs with the provided data. +/* MONGOCRYPT_KEY_LEN is 96 bytes (defined in mongocrypt-crypto-private.h). */ +#define FUZZ_KEY_LEN 96 + +/* Maximum number of state transitions to prevent infinite loops. */ +#define MAX_STATE_TRANSITIONS 32 + +/* Operation types selected by the first byte of fuzz input. */ +enum { + OP_ENCRYPT = 0, + OP_DECRYPT = 1, + OP_EXPLICIT_ENCRYPT = 2, + OP_EXPLICIT_DECRYPT = 3, + OP_DATAKEY = 4, + OP_REWRAP_MANY_DATAKEY = 5, + OP_EXPLICIT_ENCRYPT_EXPRESSION = 6, + OP_COUNT = 7, +}; + +/* Helper: consume bytes from the fuzz input. */ +static const uint8_t *fuzz_consume(const uint8_t **data, size_t *remaining, size_t n) { + if (*remaining < n) { + return NULL; + } + const uint8_t *ptr = *data; + *data += n; + *remaining -= n; + return ptr; +} + +/* Helper: consume one byte. */ +static int fuzz_consume_byte(const uint8_t **data, size_t *remaining) { + const uint8_t *p = fuzz_consume(data, remaining, 1); + return p ? *p : -1; +} + +/* + * Drive the context state machine, feeding fuzzed data at each state. + * Returns when the context reaches DONE, ERROR, or we run out of input. */ +static void drive_ctx(mongocrypt_ctx_t *ctx, const uint8_t **data, size_t *remaining) { + mongocrypt_ctx_state_t state; + int transitions = 0; + + while (transitions++ < MAX_STATE_TRANSITIONS) { + state = mongocrypt_ctx_state(ctx); + + switch (state) { + case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO_WITH_DB: + case MONGOCRYPT_CTX_NEED_MONGO_COLLINFO: + case MONGOCRYPT_CTX_NEED_MONGO_MARKINGS: + case MONGOCRYPT_CTX_NEED_MONGO_KEYS: { + /* Feed fuzzed BSON-like data as a mongo response. */ + size_t feed_len = 0; + const uint8_t *len_bytes = fuzz_consume(data, remaining, 2); + if (!len_bytes) { + return; + } + feed_len = (size_t)len_bytes[0] | ((size_t)len_bytes[1] << 8); + if (feed_len > *remaining) { + feed_len = *remaining; + } + if (feed_len > 0) { + const uint8_t *feed_data = fuzz_consume(data, remaining, feed_len); + if (feed_data) { + mongocrypt_binary_t *bin = + mongocrypt_binary_new_from_data((uint8_t *)feed_data, (uint32_t)feed_len); + if (bin) { + mongocrypt_ctx_mongo_feed(ctx, bin); + mongocrypt_binary_destroy(bin); + } + } + } + mongocrypt_ctx_mongo_done(ctx); + break; + } + + case MONGOCRYPT_CTX_NEED_KMS_CREDENTIALS: { + /* Feed fuzzed KMS provider credentials. */ + size_t feed_len = 0; + const uint8_t *len_bytes = fuzz_consume(data, remaining, 2); + if (!len_bytes) { + return; + } + feed_len = (size_t)len_bytes[0] | ((size_t)len_bytes[1] << 8); + if (feed_len > *remaining) { + feed_len = *remaining; + } + if (feed_len > 0) { + const uint8_t *feed_data = fuzz_consume(data, remaining, feed_len); + if (feed_data) { + mongocrypt_binary_t *bin = + mongocrypt_binary_new_from_data((uint8_t *)feed_data, (uint32_t)feed_len); + if (bin) { + mongocrypt_ctx_provide_kms_providers(ctx, bin); + mongocrypt_binary_destroy(bin); + } + } + } else { + /* Provide empty doc to advance past this state. */ + uint8_t empty_bson[] = {5, 0, 0, 0, 0}; /* empty BSON document */ + mongocrypt_binary_t *bin = mongocrypt_binary_new_from_data(empty_bson, sizeof(empty_bson)); + if (bin) { + mongocrypt_ctx_provide_kms_providers(ctx, bin); + mongocrypt_binary_destroy(bin); + } + } + break; + } + + case MONGOCRYPT_CTX_NEED_KMS: { + /* Iterate KMS contexts and feed fuzzed response data. */ + mongocrypt_kms_ctx_t *kms_ctx; + while ((kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx)) != NULL) { + uint32_t bytes_needed = mongocrypt_kms_ctx_bytes_needed(kms_ctx); + if (bytes_needed == 0) { + continue; + } + size_t feed_len = bytes_needed; + if (feed_len > *remaining) { + feed_len = *remaining; + } + if (feed_len == 0) { + return; + } + const uint8_t *feed_data = fuzz_consume(data, remaining, feed_len); + if (feed_data) { + mongocrypt_binary_t *bin = + mongocrypt_binary_new_from_data((uint8_t *)feed_data, (uint32_t)feed_len); + if (bin) { + mongocrypt_kms_ctx_feed(kms_ctx, bin); + mongocrypt_binary_destroy(bin); + } + } + } + mongocrypt_ctx_kms_done(ctx); + break; + } + + case MONGOCRYPT_CTX_READY: { + mongocrypt_binary_t *out = mongocrypt_binary_new(); + if (out) { + mongocrypt_ctx_finalize(ctx, out); + mongocrypt_binary_destroy(out); + } + break; + } + + case MONGOCRYPT_CTX_DONE: + case MONGOCRYPT_CTX_ERROR: + default: return; + } + } +} + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - mongocrypt_t *crypt = NULL; - mongocrypt_binary_t *input_bin = NULL; - - /* Minimum size check to avoid trivial inputs */ - if (size < 10) { + /* + * Need at least 1 byte for op selection + some data to feed. + * Trivially small inputs won't exercise meaningful code paths. + */ + if (size < 5) { return 0; } - - /* Initialize mongocrypt handle */ - crypt = mongocrypt_new(); + + const uint8_t *cursor = data; + size_t remaining = size; + + /* Consume first byte to select operation. */ + int op = fuzz_consume_byte(&cursor, &remaining) % OP_COUNT; + + /* Set up mongocrypt_t with a local KMS provider. */ + mongocrypt_t *crypt = mongocrypt_new(); if (!crypt) { return 0; } - - /* TODO: Set up KMS providers and other configuration */ - /* For now, this is a placeholder that just initializes the library */ - - /* Attempt to initialize - may fail without proper configuration */ - /* This is expected in the placeholder version */ - mongocrypt_init(crypt); - - /* Create a binary view of the input data */ - input_bin = mongocrypt_binary_new_from_data((uint8_t *)data, size); - if (!input_bin) { + + /* Configure local KMS provider with a fixed 96-byte key. */ + uint8_t local_key[FUZZ_KEY_LEN]; + memset(local_key, 0xAB, FUZZ_KEY_LEN); + mongocrypt_binary_t *key_bin = mongocrypt_binary_new_from_data(local_key, FUZZ_KEY_LEN); + if (!key_bin) { mongocrypt_destroy(crypt); return 0; } - - /* TODO: Add fuzzing targets here, such as: - * - * Example 1: Fuzz encryption context initialization - * ------------------------------------------------ - * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - * if (ctx) { - * // Use fuzzed data as a BSON command - * mongocrypt_ctx_encrypt_init(ctx, "testdb", -1, input_bin); - * mongocrypt_ctx_destroy(ctx); - * } - * - * Example 2: Fuzz decryption - * -------------------------- - * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - * if (ctx) { - * // Use fuzzed data as encrypted BSON - * mongocrypt_ctx_decrypt_init(ctx, input_bin); - * mongocrypt_ctx_destroy(ctx); - * } - * - * Example 3: Fuzz explicit encryption - * ----------------------------------- - * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - * if (ctx) { - * // Set algorithm and key - * mongocrypt_ctx_setopt_algorithm(ctx, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", -1); - * // Use fuzzed data as plaintext value - * mongocrypt_ctx_explicit_encrypt_init(ctx, input_bin); - * mongocrypt_ctx_destroy(ctx); - * } - * - * Example 4: Fuzz KMS context feed - * -------------------------------- - * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - * if (ctx) { - * // ... set up context to NEED_KMS state ... - * mongocrypt_kms_ctx_t *kms_ctx = mongocrypt_ctx_next_kms_ctx(ctx); - * if (kms_ctx) { - * // Feed fuzzed KMS response data - * mongocrypt_kms_ctx_feed(kms_ctx, input_bin); - * } - * mongocrypt_ctx_destroy(ctx); - * } - * - * Example 5: Fuzz BSON document feeding - * ------------------------------------- - * mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - * if (ctx) { - * // ... set up context to NEED_MONGO_* state ... - * // Feed fuzzed BSON documents - * mongocrypt_ctx_mongo_feed(ctx, input_bin); - * mongocrypt_ctx_mongo_done(ctx); - * mongocrypt_ctx_destroy(ctx); - * } - * - * Note: Proper fuzzing requires: - * 1. Setting up KMS providers with mongocrypt_setopt_kms_providers() - * 2. Handling different input sizes and formats - * 3. Partitioning input data for multiple parameters - * 4. Using seed corpus with valid BSON documents - * 5. Adding a dictionary for BSON field names - */ + mongocrypt_setopt_kms_provider_local(crypt, key_bin); + mongocrypt_binary_destroy(key_bin); + + if (!mongocrypt_init(crypt)) { + mongocrypt_destroy(crypt); + return 0; + } + + mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); + if (!ctx) { + mongocrypt_destroy(crypt); + return 0; + } + + /* Create a binary view of remaining fuzz data for init calls. */ + mongocrypt_binary_t *input_bin = NULL; + if (remaining > 0) { + input_bin = mongocrypt_binary_new_from_data((uint8_t *)cursor, (uint32_t)remaining); + } + + bool init_ok = false; + + switch (op) { + case OP_ENCRYPT: + if (input_bin) { + init_ok = mongocrypt_ctx_encrypt_init(ctx, "test", -1, input_bin); + } + break; + + case OP_DECRYPT: + if (input_bin) { + init_ok = mongocrypt_ctx_decrypt_init(ctx, input_bin); + } + break; + + case OP_EXPLICIT_ENCRYPT: { + /* Set a fixed key id (16-byte UUID). */ + uint8_t key_id[16]; + memset(key_id, 0x61, sizeof(key_id)); /* matches "YWFhYWFhYWFhYWFhYWFhYQ==" */ + mongocrypt_binary_t *kid_bin = mongocrypt_binary_new_from_data(key_id, sizeof(key_id)); + if (kid_bin) { + mongocrypt_ctx_setopt_key_id(ctx, kid_bin); + mongocrypt_binary_destroy(kid_bin); + } + mongocrypt_ctx_setopt_algorithm(ctx, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", -1); + if (input_bin) { + init_ok = mongocrypt_ctx_explicit_encrypt_init(ctx, input_bin); + } + break; + } + + case OP_EXPLICIT_DECRYPT: + if (input_bin) { + init_ok = mongocrypt_ctx_explicit_decrypt_init(ctx, input_bin); + } + break; + + case OP_DATAKEY: { + /* Set up key encryption key for local provider. */ + uint8_t kek_bson[] = { + /* {"provider": "local"} as BSON */ + 0x1b, 0x00, 0x00, 0x00, /* doc len = 27 */ + 0x02, /* type: string */ + 'p', 'r', 'o', 'v', 'i', 'd', 'e', 'r', 0x00, /* key */ + 0x06, 0x00, 0x00, 0x00, /* string len = 6 */ + 'l', 'o', 'c', 'a', 'l', 0x00, /* value */ + 0x00 /* doc terminator */ + }; + mongocrypt_binary_t *kek_bin = mongocrypt_binary_new_from_data(kek_bson, sizeof(kek_bson)); + if (kek_bin) { + mongocrypt_ctx_setopt_key_encryption_key(ctx, kek_bin); + mongocrypt_binary_destroy(kek_bin); + } + init_ok = mongocrypt_ctx_datakey_init(ctx); + break; + } + + case OP_REWRAP_MANY_DATAKEY: + if (input_bin) { + init_ok = mongocrypt_ctx_rewrap_many_datakey_init(ctx, input_bin); + } + break; + + case OP_EXPLICIT_ENCRYPT_EXPRESSION: { + uint8_t key_id[16]; + memset(key_id, 0x61, sizeof(key_id)); + mongocrypt_binary_t *kid_bin = mongocrypt_binary_new_from_data(key_id, sizeof(key_id)); + if (kid_bin) { + mongocrypt_ctx_setopt_key_id(ctx, kid_bin); + mongocrypt_binary_destroy(kid_bin); + } + mongocrypt_ctx_setopt_algorithm(ctx, "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", -1); + if (input_bin) { + init_ok = mongocrypt_ctx_explicit_encrypt_expression_init(ctx, input_bin); + } + break; + } + + default: break; + } + + if (input_bin) { + mongocrypt_binary_destroy(input_bin); + } + + /* If initialization succeeded, drive the state machine with fuzzed data. */ + if (init_ok) { + drive_ctx(ctx, &cursor, &remaining); + } - /* Cleanup */ - mongocrypt_binary_destroy(input_bin); + mongocrypt_ctx_destroy(ctx); mongocrypt_destroy(crypt); return 0; From 3d3aa72e061289bb7f1830a0b762b7a11690046a Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Wed, 8 Apr 2026 13:45:52 -0700 Subject: [PATCH 3/5] corpus directory --- .../fuzz_mongocrypt_corpus/op0_encrypt_aggregate | Bin 0 -> 26 bytes test/data/fuzz_mongocrypt_corpus/op0_encrypt_find | Bin 0 -> 21 bytes .../fuzz_mongocrypt_corpus/op0_encrypt_insert | Bin 0 -> 23 bytes test/data/fuzz_mongocrypt_corpus/op0_with_extra | Bin 0 -> 149 bytes test/data/fuzz_mongocrypt_corpus/op1_decrypt_doc | Bin 0 -> 23 bytes .../data/fuzz_mongocrypt_corpus/op1_decrypt_empty | Bin 0 -> 6 bytes test/data/fuzz_mongocrypt_corpus/op1_with_extra | Bin 0 -> 149 bytes .../fuzz_mongocrypt_corpus/op2_explicit_encrypt | Bin 0 -> 19 bytes .../op2_explicit_encrypt_num | Bin 0 -> 13 bytes test/data/fuzz_mongocrypt_corpus/op2_with_extra | Bin 0 -> 149 bytes .../fuzz_mongocrypt_corpus/op3_explicit_decrypt | Bin 0 -> 18 bytes test/data/fuzz_mongocrypt_corpus/op3_with_extra | Bin 0 -> 149 bytes test/data/fuzz_mongocrypt_corpus/op4_datakey | Bin 0 -> 6 bytes test/data/fuzz_mongocrypt_corpus/op4_with_extra | Bin 0 -> 134 bytes .../op5_rewrap_empty_filter | Bin 0 -> 6 bytes .../data/fuzz_mongocrypt_corpus/op5_rewrap_filter | Bin 0 -> 26 bytes test/data/fuzz_mongocrypt_corpus/op5_with_extra | Bin 0 -> 149 bytes test/data/fuzz_mongocrypt_corpus/op6_encrypt_expr | Bin 0 -> 18 bytes test/data/fuzz_mongocrypt_corpus/op6_with_extra | Bin 0 -> 149 bytes 19 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/data/fuzz_mongocrypt_corpus/op0_encrypt_aggregate create mode 100644 test/data/fuzz_mongocrypt_corpus/op0_encrypt_find create mode 100644 test/data/fuzz_mongocrypt_corpus/op0_encrypt_insert create mode 100644 test/data/fuzz_mongocrypt_corpus/op0_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op1_decrypt_doc create mode 100644 test/data/fuzz_mongocrypt_corpus/op1_decrypt_empty create mode 100644 test/data/fuzz_mongocrypt_corpus/op1_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op2_explicit_encrypt create mode 100644 test/data/fuzz_mongocrypt_corpus/op2_explicit_encrypt_num create mode 100644 test/data/fuzz_mongocrypt_corpus/op2_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op3_explicit_decrypt create mode 100644 test/data/fuzz_mongocrypt_corpus/op3_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op4_datakey create mode 100644 test/data/fuzz_mongocrypt_corpus/op4_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op5_rewrap_empty_filter create mode 100644 test/data/fuzz_mongocrypt_corpus/op5_rewrap_filter create mode 100644 test/data/fuzz_mongocrypt_corpus/op5_with_extra create mode 100644 test/data/fuzz_mongocrypt_corpus/op6_encrypt_expr create mode 100644 test/data/fuzz_mongocrypt_corpus/op6_with_extra diff --git a/test/data/fuzz_mongocrypt_corpus/op0_encrypt_aggregate b/test/data/fuzz_mongocrypt_corpus/op0_encrypt_aggregate new file mode 100644 index 0000000000000000000000000000000000000000..5919e9629c5f456814c186ba2906d3b41a34fb0e GIT binary patch literal 26 fcmZROWME)mN=#2LN=;8JNo8OKa!XQ+OBfgcMGysu literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op0_encrypt_find b/test/data/fuzz_mongocrypt_corpus/op0_encrypt_find new file mode 100644 index 0000000000000000000000000000000000000000..648e4b31c29ebed595cd7c46266a989b7d209ce5 GIT binary patch literal 21 acmZP&VPIfjO3TbkVPFNaN>YnU7#IK_&I5e_ literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op0_encrypt_insert b/test/data/fuzz_mongocrypt_corpus/op0_encrypt_insert new file mode 100644 index 0000000000000000000000000000000000000000..569766a637f46512a23926d52d4ec21573bed3bc GIT binary patch literal 23 ccmZP&V_;xl%FHWHEh=GP1+q(0i%S?7055<9a{vGU literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op0_with_extra b/test/data/fuzz_mongocrypt_corpus/op0_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..c2c9f38a82d0a9f7c44ea4c69b12942d214aa3f5 GIT binary patch literal 149 zcmV;G0BZjL6aWAK0%mD$WB>&K004Alb94XzqZ|gkMm{+syc(^&ov z29Gkgrn8G{IC7Rp&)xyT&M2~MS4Sr|^}|*g7hxD%*jSoF&I`JAi68mwU=)o@)S#Z? z++33}v=IxbI>k!-6xuu88DU3sqSaj~URpJ)M6>U%r4pN>D~P&2DRyB+{p_Q#jySJV D?omD| literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op1_decrypt_doc b/test/data/fuzz_mongocrypt_corpus/op1_decrypt_doc new file mode 100644 index 0000000000000000000000000000000000000000..3ecb78dd5254cba2d99aa4c6db7c7ef11dc4882a GIT binary patch literal 23 ccmZP+V_;xlO3O^mNnv0EGRqQkN>dpa04r1kK>z>% literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op1_decrypt_empty b/test/data/fuzz_mongocrypt_corpus/op1_decrypt_empty new file mode 100644 index 0000000000000000000000000000000000000000..770f5196b3639480f03c873fb46f3917e51785b2 GIT binary patch literal 6 LcmZQ%WdH&I03`qi literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op1_with_extra b/test/data/fuzz_mongocrypt_corpus/op1_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..22a01fba805e295900f651ef75cc4c9bd4b9e3ed GIT binary patch literal 149 zcmV;G0BZjM6aWAK0%mD$WB>&K004Alb94Xz*TlvSI?@QvP-a9BHtF$^;I~jVr+;PQ z>ZElfLnA-FkBLLSm2KsfW?VS`|09Ba7r_kN94DYC%C2mf5Mp7N|96K#_>J800c^Ir$6>01_wyrvLx| literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op2_explicit_encrypt_num b/test/data/fuzz_mongocrypt_corpus/op2_explicit_encrypt_num new file mode 100644 index 0000000000000000000000000000000000000000..315710d70d29f0b04e00e78199e298b890fa7000 GIT binary patch literal 13 ScmZSLVPIeoC}Yq9QVakELjb=3 literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op2_with_extra b/test/data/fuzz_mongocrypt_corpus/op2_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..c887766a25d24545aec9071b0645199111dd91f9 GIT binary patch literal 149 zcmV;G0BZjN6aWAK0%mD$WB>&K004Alb94Xz;QZ9iPCX7i;F0na6}x=U5&FW2#2_H3 zdGd@ZLx literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op3_explicit_decrypt b/test/data/fuzz_mongocrypt_corpus/op3_explicit_decrypt new file mode 100644 index 0000000000000000000000000000000000000000..fc491fb628aedfe0e6b901b6d7b43ff82cc0d9a8 GIT binary patch literal 18 XcmZP;WME)mDq~;;QYncgi3|(?4s8M~ literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op3_with_extra b/test/data/fuzz_mongocrypt_corpus/op3_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..f25277da9a9774ce8f76b7edbce9933ea22d3c90 GIT binary patch literal 149 zcmV;G0BZjO6aWAK0%mD$WB>&K004Alb94XzKi}*Van9V^j2R53|BBWF_!sTK**q#} ze0w(CX6FwoVF3JNL+!}ObV+Qo^}GJaj;XhNCooGl_YRc2iVwd~4h)rgfa}_aC=T_4 z6xlBkm=UbpJZB#9m6)5(0If|B(L~ DZ74&N literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op4_datakey b/test/data/fuzz_mongocrypt_corpus/op4_datakey new file mode 100644 index 0000000000000000000000000000000000000000..6595db2eda72c08f3aea06c7e91e139589f9f160 GIT binary patch literal 6 LcmZQ!WdH&I05<>% literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op4_with_extra b/test/data/fuzz_mongocrypt_corpus/op4_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..8f835e8cf6da039842a340cb372743b14706b177 GIT binary patch literal 134 zcmV;10D1oe1poj501}BdfkPqgS>X}kK3+&DaMg>nO`QAu&Zmb0s?m*1?x`B^;37jE z;~Tz>Cq+n_HMdtZucF#Rfqg*d=-3SxqHNSg3jkHcA*1?3DZO!xv~7<9924>4u;(U= o3DsVejU{d%3Qk?-?#uYx3g%liu0N_8TE~y#-)Q-szbEYOJiIkPC;$Ke literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op5_rewrap_empty_filter b/test/data/fuzz_mongocrypt_corpus/op5_rewrap_empty_filter new file mode 100644 index 0000000000000000000000000000000000000000..cbfc01ec178bb0b7a6de30760eff3a9db1d6d04a GIT binary patch literal 6 LcmZQ&WdH&I06hQ; literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op5_rewrap_filter b/test/data/fuzz_mongocrypt_corpus/op5_rewrap_filter new file mode 100644 index 0000000000000000000000000000000000000000..ee46c24ff5b897c7e5acdbb13983140a5b0a1d81 GIT binary patch literal 26 fcmZRTWME)mDk#b?%S=fvVqgREa`KZCa~K!^MpOlz literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op5_with_extra b/test/data/fuzz_mongocrypt_corpus/op5_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..9dfa6c21dfc86a3a550d5402799ff73ffa214334 GIT binary patch literal 149 zcmV;G0BZjQ6aWAK0%mD$WB>&K004Alb94Xz-6{FZ&n@9;1}(hoRmkpX&8ywK&p$*c z$+H__-wE4zI5W}fby-f*%ilUV2B_(Cm|_WMH# z8<=lcy2(&)oBM(tV&;=DK?}8S0EO(%iLbcuy}YY3U2YNbs_RsrP^sA;xaLfOPpW8B DCsapQ literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op6_encrypt_expr b/test/data/fuzz_mongocrypt_corpus/op6_encrypt_expr new file mode 100644 index 0000000000000000000000000000000000000000..0d6dce3daad783dca81b9c5896bc8b8bf684b13d GIT binary patch literal 18 XcmZP*WME)mDq~;;QmGXMMGOo84?hA_ literal 0 HcmV?d00001 diff --git a/test/data/fuzz_mongocrypt_corpus/op6_with_extra b/test/data/fuzz_mongocrypt_corpus/op6_with_extra new file mode 100644 index 0000000000000000000000000000000000000000..59e3cb044b9d6914290be7c5cdff582bf1c59da4 GIT binary patch literal 149 zcmV;G0BZjR6aWAK0%mD$WB>&K004Alb94XzXR=F;ATevI@L{dL=PaF(O=pbN08L3X zZpoCJr&4!uajZ6idCTVY%e*V76-a}rqMlV3(euDOtWP=4G9?BJKKpr`*~AibYT}`j zF}Jc}er7%;r?3I!z}&^+8^>%oEzR|^gmw&$KkC^ZbRx}ms)ziKm{Ie= Date: Wed, 8 Apr 2026 13:46:38 -0700 Subject: [PATCH 4/5] copy corpus dir --- oss-fuzz/build.sh | 6 ++++-- oss-fuzz/test_build_local.sh | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh index ec9b51c42..8a55c8a23 100755 --- a/oss-fuzz/build.sh +++ b/oss-fuzz/build.sh @@ -57,6 +57,8 @@ $CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_mongocrypt.o \ _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ -Wl,--end-group -# TODO: Add seed corpus and dictionary files -# cp ../test/data/seed_corpus/* $OUT/fuzz_mongocrypt_seed_corpus/ +# Package seed corpus for fuzz_mongocrypt +mkdir -p $OUT/fuzz_mongocrypt_seed_corpus +cp ../test/data/fuzz_mongocrypt_corpus/* $OUT/fuzz_mongocrypt_seed_corpus/ +zip -j $OUT/fuzz_mongocrypt_seed_corpus.zip $OUT/fuzz_mongocrypt_seed_corpus/* diff --git a/oss-fuzz/test_build_local.sh b/oss-fuzz/test_build_local.sh index 31074f03f..a4e44e298 100755 --- a/oss-fuzz/test_build_local.sh +++ b/oss-fuzz/test_build_local.sh @@ -97,7 +97,12 @@ echo "To run a fuzzer:" echo " $OUT/fuzz_kms -max_total_time=60" echo " $OUT/fuzz_mongocrypt -max_total_time=60" echo "" -echo "To run with a corpus directory:" -echo " mkdir -p corpus" -echo " $OUT/fuzz_kms corpus/ -max_total_time=60" +echo "=== Copying seed corpus ===" +mkdir -p "$OUT/fuzz_mongocrypt_seed_corpus" +cp ../test/data/fuzz_mongocrypt_corpus/* "$OUT/fuzz_mongocrypt_seed_corpus/" + +echo "" +echo "To run with the seed corpus:" +echo " $OUT/fuzz_mongocrypt $OUT/fuzz_mongocrypt_seed_corpus -max_total_time=60" +echo " $OUT/fuzz_kms -max_total_time=60" From 695fd85c6f178e8c5456ac12f615dc49878baa06 Mon Sep 17 00:00:00 2001 From: mdb-ad <198671546+mdb-ad@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:21:06 -0700 Subject: [PATCH 5/5] no need to commit oss-fuzz files here --- oss-fuzz/CHECKLIST.md | 192 ---------------------------------- oss-fuzz/Dockerfile | 33 ------ oss-fuzz/INTEGRATION_GUIDE.md | 170 ------------------------------ oss-fuzz/README.md | 81 -------------- oss-fuzz/SUMMARY.md | 165 ----------------------------- oss-fuzz/build.sh | 64 ------------ oss-fuzz/project.yaml | 24 ----- oss-fuzz/test_build_local.sh | 108 ------------------- 8 files changed, 837 deletions(-) delete mode 100644 oss-fuzz/CHECKLIST.md delete mode 100644 oss-fuzz/Dockerfile delete mode 100644 oss-fuzz/INTEGRATION_GUIDE.md delete mode 100644 oss-fuzz/README.md delete mode 100644 oss-fuzz/SUMMARY.md delete mode 100755 oss-fuzz/build.sh delete mode 100644 oss-fuzz/project.yaml delete mode 100755 oss-fuzz/test_build_local.sh diff --git a/oss-fuzz/CHECKLIST.md b/oss-fuzz/CHECKLIST.md deleted file mode 100644 index 3a1979e49..000000000 --- a/oss-fuzz/CHECKLIST.md +++ /dev/null @@ -1,192 +0,0 @@ -# OSS-Fuzz Implementation Checklist - -Use this checklist to track progress on implementing the OSS-Fuzz integration. - -## Phase 1: Setup and Verification ✅ - -- [x] Create OSS-Fuzz configuration files - - [x] Dockerfile - - [x] build.sh - - [x] project.yaml -- [x] Create placeholder fuzzer (`test/fuzz_mongocrypt.c`) -- [x] Create documentation - - [x] README.md - - [x] INTEGRATION_GUIDE.md - - [x] SUMMARY.md -- [x] Create local testing script -- [x] Make scripts executable - -## Phase 2: Local Testing - -- [ ] Test local build - ```bash - cd oss-fuzz - ./test_build_local.sh - ``` -- [ ] Verify fuzzers compile without errors -- [ ] Run placeholder fuzzer to ensure it executes - ```bash - ./out/fuzz_mongocrypt -max_total_time=10 - ``` -- [ ] Verify existing fuzz_kms works - ```bash - ./out/fuzz_kms -max_total_time=10 - ``` - -## Phase 3: Implement Fuzzing Logic - -### 3.1 Basic Implementation -- [ ] Add KMS provider configuration - - [ ] Create minimal valid KMS config - - [ ] Handle initialization errors gracefully -- [ ] Implement basic encryption fuzzing - - [ ] Fuzz `mongocrypt_ctx_encrypt_init()` - - [ ] Handle BSON parsing errors - - [ ] Test with valid BSON samples -- [ ] Implement basic decryption fuzzing - - [ ] Fuzz `mongocrypt_ctx_decrypt_init()` - - [ ] Handle invalid ciphertext gracefully - -### 3.2 Advanced Implementation -- [ ] Add explicit encryption fuzzing - - [ ] Fuzz `mongocrypt_ctx_explicit_encrypt_init()` - - [ ] Test different algorithms - - [ ] Test different key IDs -- [ ] Add KMS context fuzzing - - [ ] Fuzz `mongocrypt_kms_ctx_feed()` - - [ ] Test different KMS providers -- [ ] Add BSON document feeding - - [ ] Fuzz `mongocrypt_ctx_mongo_feed()` - - [ ] Test collection info documents - - [ ] Test key documents - -### 3.3 Input Partitioning -- [ ] Implement input splitting strategy - - [ ] Reserve bytes for operation selection - - [ ] Reserve bytes for configuration - - [ ] Reserve bytes for BSON data -- [ ] Add input validation - - [ ] Check minimum sizes - - [ ] Validate BSON structure -- [ ] Handle edge cases - - [ ] Empty inputs - - [ ] Very large inputs - - [ ] Malformed BSON - -## Phase 4: Seed Corpus - -- [ ] Create seed corpus directory - ```bash - mkdir -p oss-fuzz/seed_corpus - ``` -- [ ] Add valid BSON documents - - [ ] Simple documents - - [ ] Nested documents - - [ ] Arrays - - [ ] All BSON types -- [ ] Add encrypted data samples - - [ ] FLE1 encrypted values - - [ ] FLE2 encrypted values - - [ ] Different algorithms -- [ ] Add KMS response samples - - [ ] AWS KMS responses - - [ ] Azure responses - - [ ] GCP responses - - [ ] Local KMS responses - -## Phase 5: Dictionary - -- [ ] Create BSON dictionary file - ```bash - touch oss-fuzz/mongocrypt.dict - ``` -- [ ] Add common field names - - [ ] `"_id"` - - [ ] `"keyId"` - - [ ] `"algorithm"` - - [ ] `"value"` - - [ ] `"v"` - - [ ] Add more from test data -- [ ] Add algorithm names - - [ ] `"AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"` - - [ ] `"AEAD_AES_256_CBC_HMAC_SHA_512-Random"` - - [ ] Add FLE2 algorithms - -## Phase 6: Testing with Sanitizers - -- [ ] Test with AddressSanitizer (ASan) - ```bash - # In test_build_local.sh, CFLAGS already includes ASan - ./test_build_local.sh - ./out/fuzz_mongocrypt -max_total_time=300 - ``` -- [ ] Test with UndefinedBehaviorSanitizer (UBSan) - ```bash - # Modify CFLAGS in test_build_local.sh - export CFLAGS="-g -O1 -fsanitize=undefined,fuzzer-no-link" - ``` -- [ ] Test with MemorySanitizer (MSan) - ```bash - # Requires clang and special build - export CFLAGS="-g -O1 -fsanitize=memory,fuzzer-no-link" - ``` -- [ ] Fix any issues found - - [ ] Document expected errors - - [ ] Fix actual bugs - - [ ] Add suppressions if needed - -## Phase 7: OSS-Fuzz Integration Testing - -- [ ] Clone OSS-Fuzz repository - ```bash - git clone https://github.com/google/oss-fuzz.git - ``` -- [ ] Copy files to OSS-Fuzz - ```bash - cp -r oss-fuzz/* oss-fuzz-repo/projects/libmongocrypt/ - ``` -- [ ] Build with OSS-Fuzz infrastructure - ```bash - python infra/helper.py build_image libmongocrypt - python infra/helper.py build_fuzzers libmongocrypt - ``` -- [ ] Run fuzzers - ```bash - python infra/helper.py run_fuzzer libmongocrypt fuzz_mongocrypt - ``` -- [ ] Check coverage - ```bash - python infra/helper.py coverage libmongocrypt - ``` -- [ ] Review coverage report - - [ ] Identify uncovered code - - [ ] Add tests for uncovered paths - -## Phase 8: Submission - -- [ ] Update project.yaml with correct contacts - - [ ] Verify primary_contact email - - [ ] Add auto_ccs emails -- [ ] Create PR to OSS-Fuzz - - [ ] Fork OSS-Fuzz repository - - [ ] Create branch - - [ ] Commit files - - [ ] Submit pull request -- [ ] Address review comments -- [ ] Wait for approval and merge - -## Phase 9: Monitoring - -- [ ] Set up bug notifications -- [ ] Monitor OSS-Fuzz dashboard -- [ ] Triage reported issues -- [ ] Fix bugs found by fuzzing -- [ ] Update seed corpus based on findings - -## Notes - -- Mark items as complete with `[x]` -- Add notes or blockers inline -- Update this checklist as needed -- Keep SUMMARY.md in sync with progress - diff --git a/oss-fuzz/Dockerfile b/oss-fuzz/Dockerfile deleted file mode 100644 index 547f7c67d..000000000 --- a/oss-fuzz/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2024 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. -# -################################################################################ - -FROM gcr.io/oss-fuzz-base/base-builder - -# Install dependencies -RUN apt-get update && apt-get install -y \ - cmake \ - pkg-config \ - libssl-dev - -# Clone the repository -RUN git clone --depth 1 https://github.com/mongodb/libmongocrypt.git libmongocrypt - -# Set working directory -WORKDIR libmongocrypt - -# Copy build script -COPY build.sh $SRC/ - diff --git a/oss-fuzz/INTEGRATION_GUIDE.md b/oss-fuzz/INTEGRATION_GUIDE.md deleted file mode 100644 index 50e5b402d..000000000 --- a/oss-fuzz/INTEGRATION_GUIDE.md +++ /dev/null @@ -1,170 +0,0 @@ -# OSS-Fuzz Integration Guide for libmongocrypt - -## Quick Start - -This guide explains how to integrate libmongocrypt with OSS-Fuzz. - -## What Has Been Created - -### 1. Fuzzing Entry Points - -#### `test/fuzz_mongocrypt.c` (NEW - Placeholder) -A placeholder fuzzer for the main libmongocrypt APIs. This file contains: -- Basic libFuzzer entry point (`LLVMFuzzerTestOneInput`) -- Extensive TODO comments with examples of how to fuzz different APIs -- Proper initialization and cleanup - -**Status**: Placeholder only - needs actual fuzzing logic implementation - -#### `test/fuzz_kms.c` (Existing) -Already exists and fuzzes KMS message parsing. - -### 2. OSS-Fuzz Configuration Files - -All files are in the `oss-fuzz/` directory: - -#### `Dockerfile` -- Defines the build environment -- Installs dependencies (cmake, pkg-config, libssl-dev) -- Clones the libmongocrypt repository - -#### `build.sh` -- Builds libmongocrypt with appropriate flags -- Compiles fuzzing targets -- Links against libFuzzer - -#### `project.yaml` -- Project metadata (homepage, contacts, language) -- Sanitizer configuration (ASan, UBSan, MSan) -- Fuzzing engine configuration (libFuzzer, AFL, Honggfuzz) - -## Next Steps - -### Step 1: Implement the Fuzzer Logic - -Edit `test/fuzz_mongocrypt.c` to add actual fuzzing logic. See the TODO comments for examples. - -Key considerations: -- **Input partitioning**: Split the fuzzed input into multiple parts for different parameters -- **State management**: Handle the mongocrypt state machine properly -- **Error handling**: Don't crash on expected errors -- **Coverage**: Target multiple code paths - -Example implementation approach: -```c -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - if (size < 100) return 0; - - // Partition input - const uint8_t *kms_config = data; - size_t kms_config_size = 50; - const uint8_t *bson_data = data + 50; - size_t bson_size = size - 50; - - // Initialize with fuzzed KMS config - mongocrypt_t *crypt = mongocrypt_new(); - mongocrypt_binary_t *kms_bin = mongocrypt_binary_new_from_data( - (uint8_t *)kms_config, kms_config_size); - mongocrypt_setopt_kms_providers(crypt, kms_bin); - mongocrypt_init(crypt); - - // Fuzz encryption - mongocrypt_ctx_t *ctx = mongocrypt_ctx_new(crypt); - mongocrypt_binary_t *doc_bin = mongocrypt_binary_new_from_data( - (uint8_t *)bson_data, bson_size); - mongocrypt_ctx_encrypt_init(ctx, "db", -1, doc_bin); - - // Cleanup - mongocrypt_binary_destroy(doc_bin); - mongocrypt_binary_destroy(kms_bin); - mongocrypt_ctx_destroy(ctx); - mongocrypt_destroy(crypt); - - return 0; -} -``` - -### Step 2: Create Seed Corpus - -Create a directory with sample inputs: -```bash -mkdir -p oss-fuzz/seed_corpus -``` - -Add valid BSON documents and encrypted data as seed inputs. These help the fuzzer understand the input format. - -### Step 3: Create Dictionary (Optional) - -Create `oss-fuzz/mongocrypt.dict` with common BSON field names: -``` -"_id" -"keyId" -"algorithm" -"value" -"v" -"encrypted" -``` - -### Step 4: Test Locally - -```bash -# Clone OSS-Fuzz -git clone https://github.com/google/oss-fuzz.git -cd oss-fuzz - -# Create project directory -mkdir -p projects/libmongocrypt - -# Copy files -cp /path/to/libmongocrypt/oss-fuzz/* projects/libmongocrypt/ - -# Build -python infra/helper.py build_image libmongocrypt -python infra/helper.py build_fuzzers libmongocrypt - -# Run -python infra/helper.py run_fuzzer libmongocrypt fuzz_mongocrypt -- -max_total_time=60 -``` - -### Step 5: Submit to OSS-Fuzz - -1. Test all sanitizers: - ```bash - python infra/helper.py build_fuzzers --sanitizer address libmongocrypt - python infra/helper.py build_fuzzers --sanitizer undefined libmongocrypt - python infra/helper.py build_fuzzers --sanitizer memory libmongocrypt - ``` - -2. Run coverage check: - ```bash - python infra/helper.py coverage libmongocrypt - ``` - -3. Submit PR to https://github.com/google/oss-fuzz - -## Troubleshooting - -### Build Failures - -- Check that all dependencies are in the Dockerfile -- Verify library paths in build.sh -- Ensure static libraries are being built - -### Runtime Crashes - -- Use `run_fuzzer` with `-help=1` to see libFuzzer options -- Add `-print_final_stats=1` for debugging -- Check sanitizer output for actual bugs vs. expected errors - -### Low Coverage - -- Add more seed corpus files -- Implement multiple fuzzing targets -- Use coverage report to identify untested code - -## Resources - -- [OSS-Fuzz Documentation](https://google.github.io/oss-fuzz/) -- [libFuzzer Documentation](https://llvm.org/docs/LibFuzzer.html) -- [Fuzzing BSON](https://github.com/mongodb/mongo/tree/master/src/third_party/libfuzzer) - diff --git a/oss-fuzz/README.md b/oss-fuzz/README.md deleted file mode 100644 index bd098a84d..000000000 --- a/oss-fuzz/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# OSS-Fuzz Integration for libmongocrypt - -This directory contains the OSS-Fuzz integration for libmongocrypt. - -## Overview - -[OSS-Fuzz](https://github.com/google/oss-fuzz) is Google's continuous fuzzing service for open source software. This integration enables automated fuzzing of libmongocrypt to discover potential security vulnerabilities and bugs. - -## Files - -- **Dockerfile**: Defines the build environment for OSS-Fuzz -- **build.sh**: Script that builds libmongocrypt and the fuzzing targets -- **project.yaml**: OSS-Fuzz project configuration - -## Fuzzing Targets - -### fuzz_kms -Fuzzes the KMS message parsing and request creation functionality. -- Source: `test/fuzz_kms.c` -- Targets: `kms_response_parser_feed()`, `kms_request_new()` - -### fuzz_mongocrypt (Placeholder) -Main fuzzer for libmongocrypt encryption/decryption APIs. -- Source: `test/fuzz_mongocrypt.c` -- Status: **Placeholder** - needs implementation -- Planned targets: - - `mongocrypt_ctx_encrypt_init()` with fuzzed BSON commands - - `mongocrypt_ctx_decrypt_init()` with fuzzed encrypted data - - `mongocrypt_ctx_explicit_encrypt_init()` with fuzzed values - - `mongocrypt_ctx_explicit_decrypt_init()` with fuzzed ciphertext - - `mongocrypt_ctx_mongo_feed()` with fuzzed BSON documents - - `mongocrypt_kms_ctx_feed()` with fuzzed KMS responses - -## Local Testing - -To test the OSS-Fuzz integration locally: - -```bash -# Clone OSS-Fuzz -git clone https://github.com/google/oss-fuzz.git -cd oss-fuzz - -# Copy the libmongocrypt OSS-Fuzz files -cp -r /path/to/libmongocrypt/oss-fuzz projects/libmongocrypt - -# Build the Docker image -python infra/helper.py build_image libmongocrypt - -# Build the fuzzers -python infra/helper.py build_fuzzers libmongocrypt - -# Run a fuzzer -python infra/helper.py run_fuzzer libmongocrypt fuzz_kms -``` - -## Integration with OSS-Fuzz Repository - -To integrate with the official OSS-Fuzz repository: - -1. Fork the [OSS-Fuzz repository](https://github.com/google/oss-fuzz) -2. Copy the files from this directory to `projects/libmongocrypt/` in the OSS-Fuzz repo -3. Test locally using the commands above -4. Submit a pull request to the OSS-Fuzz repository - -See the [OSS-Fuzz New Project Guide](https://google.github.io/oss-fuzz/getting-started/new-project-guide/) for detailed instructions. - -## TODO - -- [ ] Implement comprehensive fuzzing logic in `fuzz_mongocrypt.c` -- [ ] Add seed corpus for better fuzzing coverage -- [ ] Add dictionary files for BSON fuzzing -- [ ] Test with all sanitizers (ASan, UBSan, MSan) -- [ ] Add additional fuzzing targets for specific APIs -- [ ] Configure continuous integration with OSS-Fuzz - -## References - -- [OSS-Fuzz Documentation](https://google.github.io/oss-fuzz/) -- [libFuzzer Tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) -- [libmongocrypt Documentation](https://github.com/mongodb/libmongocrypt) - diff --git a/oss-fuzz/SUMMARY.md b/oss-fuzz/SUMMARY.md deleted file mode 100644 index 05bf712f1..000000000 --- a/oss-fuzz/SUMMARY.md +++ /dev/null @@ -1,165 +0,0 @@ -# OSS-Fuzz Integration - Summary - -## What Was Created - -This OSS-Fuzz integration provides a foundation for continuous fuzzing of libmongocrypt through Google's OSS-Fuzz service. - -### Files Created - -``` -oss-fuzz/ -├── Dockerfile # OSS-Fuzz build environment -├── build.sh # Build script for fuzzers (executable) -├── project.yaml # OSS-Fuzz project configuration -├── README.md # Overview and usage instructions -├── INTEGRATION_GUIDE.md # Detailed integration steps -├── test_build_local.sh # Local testing script (executable) -└── SUMMARY.md # This file - -test/ -└── fuzz_mongocrypt.c # Placeholder fuzzer for main APIs -``` - -### Fuzzing Targets - -1. **fuzz_kms** (existing in `test/fuzz_kms.c`) - - Fuzzes KMS message parsing - - Targets: `kms_response_parser_feed()`, `kms_request_new()` - - Status: ✅ Ready to use - -2. **fuzz_mongocrypt** (new in `test/fuzz_mongocrypt.c`) - - Placeholder for main libmongocrypt APIs - - Status: ⚠️ Placeholder only - needs implementation - - Contains extensive TODO comments with examples - -## Current Status - -### ✅ Complete -- OSS-Fuzz configuration files (Dockerfile, build.sh, project.yaml) -- Placeholder fuzzer with detailed implementation examples -- Documentation (README, integration guide) -- Local testing script - -### ⚠️ Placeholder / TODO -- Actual fuzzing logic in `fuzz_mongocrypt.c` -- Seed corpus for better coverage -- Dictionary files for BSON fuzzing -- Additional fuzzing targets for specific APIs - -## How to Use - -### Option 1: Local Testing (Recommended First) - -```bash -cd oss-fuzz -./test_build_local.sh -``` - -This will: -- Build libmongocrypt with fuzzing instrumentation -- Compile both fuzzers -- Output binaries to `out/` directory - -Then run: -```bash -./out/fuzz_kms -max_total_time=60 -./out/fuzz_mongocrypt -max_total_time=60 -``` - -### Option 2: Test with OSS-Fuzz Infrastructure - -```bash -# Clone OSS-Fuzz -git clone https://github.com/google/oss-fuzz.git -cd oss-fuzz - -# Copy files -cp -r /path/to/libmongocrypt/oss-fuzz projects/libmongocrypt - -# Build and test -python infra/helper.py build_image libmongocrypt -python infra/helper.py build_fuzzers libmongocrypt -python infra/helper.py run_fuzzer libmongocrypt fuzz_kms -``` - -### Option 3: Submit to OSS-Fuzz (Final Step) - -After testing locally: -1. Fork https://github.com/google/oss-fuzz -2. Copy files to `projects/libmongocrypt/` -3. Test with all sanitizers -4. Submit pull request - -## Next Steps - -### Immediate (Required for Production) - -1. **Implement fuzzing logic** in `test/fuzz_mongocrypt.c` - - See TODO comments for examples - - Focus on encryption/decryption APIs - - Handle state machine properly - -2. **Create seed corpus** - - Add valid BSON documents - - Include encrypted data samples - - Store in `oss-fuzz/seed_corpus/` - -3. **Test thoroughly** - - Run with AddressSanitizer - - Run with UndefinedBehaviorSanitizer - - Run with MemorySanitizer - - Verify no false positives - -### Future Enhancements - -1. **Additional fuzzers** - - Fuzz specific FLE2 operations - - Fuzz range query encryption - - Fuzz key management operations - -2. **Improve coverage** - - Add dictionary for BSON field names - - Create structure-aware fuzzing - - Add custom mutators - -3. **Integration** - - Set up continuous fuzzing - - Configure bug reporting - - Add to CI/CD pipeline - -## Important Notes - -### Placeholder Status - -The `fuzz_mongocrypt.c` file is currently a **placeholder**. It: -- ✅ Compiles successfully -- ✅ Links with libmongocrypt -- ✅ Has proper libFuzzer entry point -- ❌ Does NOT actually fuzz anything yet -- ❌ Needs implementation before production use - -### Why Placeholder? - -As requested, this provides the structure and examples without implementing the actual fuzzing logic. This allows you to: -1. Review the approach -2. Decide which APIs to prioritize -3. Implement fuzzing logic incrementally -4. Test the build system first - -### Implementation Examples - -The placeholder includes detailed comments showing how to fuzz: -- Encryption context initialization -- Decryption operations -- Explicit encryption/decryption -- KMS context feeding -- BSON document feeding - -## Questions? - -See: -- `README.md` - Overview and quick start -- `INTEGRATION_GUIDE.md` - Detailed implementation guide -- [OSS-Fuzz Docs](https://google.github.io/oss-fuzz/) -- [libFuzzer Tutorial](https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md) - diff --git a/oss-fuzz/build.sh b/oss-fuzz/build.sh deleted file mode 100755 index 8a55c8a23..000000000 --- a/oss-fuzz/build.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/bash -eu -# Copyright 2024 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. -# -################################################################################ - -# OSS-Fuzz build script for libmongocrypt -# This script is called by OSS-Fuzz to build the fuzzing targets - -cd $SRC/libmongocrypt - -# Build the library -mkdir -p build -cd build - -# Configure with CMake -# Note: OSS-Fuzz sets CC, CXX, CFLAGS, CXXFLAGS, LIB_FUZZING_ENGINE -cmake .. \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_C_FLAGS="${CFLAGS}" \ - -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ - -DENABLE_ONLINE_TESTS=OFF \ - -DENABLE_STATIC=ON - -# Build the library -make -j$(nproc) mongocrypt_static - -# Build the fuzzers -# fuzz_kms - existing KMS fuzzer -$CC $CFLAGS -c ../test/fuzz_kms.c -I../kms-message/src -I../src -o fuzz_kms.o -$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_kms.o \ - -o $OUT/fuzz_kms \ - libmongocrypt_static.a \ - kms-message/libkms_message_static.a \ - -Wl,--start-group \ - _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ - -Wl,--end-group - -# fuzz_mongocrypt - main libmongocrypt fuzzer (placeholder) -$CC $CFLAGS -c ../test/fuzz_mongocrypt.c -I../src -I. -o fuzz_mongocrypt.o -$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_mongocrypt.o \ - -o $OUT/fuzz_mongocrypt \ - libmongocrypt_static.a \ - kms-message/libkms_message_static.a \ - -Wl,--start-group \ - _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ - -Wl,--end-group - -# Package seed corpus for fuzz_mongocrypt -mkdir -p $OUT/fuzz_mongocrypt_seed_corpus -cp ../test/data/fuzz_mongocrypt_corpus/* $OUT/fuzz_mongocrypt_seed_corpus/ -zip -j $OUT/fuzz_mongocrypt_seed_corpus.zip $OUT/fuzz_mongocrypt_seed_corpus/* - diff --git a/oss-fuzz/project.yaml b/oss-fuzz/project.yaml deleted file mode 100644 index 065264421..000000000 --- a/oss-fuzz/project.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# OSS-Fuzz project configuration for libmongocrypt -# See https://google.github.io/oss-fuzz/getting-started/new-project-guide/ - -homepage: "https://github.com/mongodb/libmongocrypt" -language: c -primary_contact: "security@mongodb.com" -auto_ccs: - - "your-team@mongodb.com" - -# Sanitizers to run -sanitizers: - - address - - undefined - - memory - -# Fuzzing engines -fuzzing_engines: - - libfuzzer - - afl - - honggfuzz - -# Build configuration -main_repo: "https://github.com/mongodb/libmongocrypt.git" - diff --git a/oss-fuzz/test_build_local.sh b/oss-fuzz/test_build_local.sh deleted file mode 100755 index a4e44e298..000000000 --- a/oss-fuzz/test_build_local.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash -# Local build test script for OSS-Fuzz integration -# This script helps test the fuzzer build without the full OSS-Fuzz infrastructure - -set -e - -echo "=== Testing OSS-Fuzz build locally ===" - -# Check if clang is available -if ! command -v clang &> /dev/null; then - echo "Error: clang is required but not found" - echo "Install with: sudo apt-get install clang" - exit 1 -fi - -# Check if clang++ is available -if ! command -v clang++ &> /dev/null; then - echo "Error: clang++ is required but not found" - echo "Install with: sudo apt-get install clang" - exit 1 -fi - -# Set up environment variables similar to OSS-Fuzz -export CC=clang -export CXX=clang++ -export CFLAGS="-g -O1 -fno-omit-frame-pointer -fsanitize=address,fuzzer-no-link" -export CXXFLAGS="-g -O1 -fno-omit-frame-pointer -fsanitize=address,fuzzer-no-link" -export LIB_FUZZING_ENGINE="-fsanitize=fuzzer" -export OUT="${OUT:-$(pwd)/out}" - -echo "Build output directory: $OUT" -mkdir -p "$OUT" - -# Get the repository root -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -echo "Repository root: $REPO_ROOT" - -cd "$REPO_ROOT" - -# Create build directory -BUILD_DIR="$REPO_ROOT/build-fuzz" -rm -rf "$BUILD_DIR" -mkdir -p "$BUILD_DIR" -cd "$BUILD_DIR" - -echo "" -echo "=== Configuring with CMake ===" -cmake .. \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_C_FLAGS="${CFLAGS}" \ - -DCMAKE_CXX_FLAGS="${CXXFLAGS}" \ - -DENABLE_ONLINE_TESTS=OFF \ - -DENABLE_STATIC=ON - -echo "" -echo "=== Building libmongocrypt ===" -make -j$(nproc) mongocrypt_static - -echo "" -echo "=== Building fuzz_kms ===" -$CC $CFLAGS -c ../test/fuzz_kms.c \ - -I../kms-message/src \ - -I../src \ - -o fuzz_kms.o - -$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_kms.o \ - -o "$OUT/fuzz_kms" \ - libmongocrypt_static.a \ - kms-message/libkms_message_static.a \ - -Wl,--start-group \ - _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ - -Wl,--end-group - -echo "" -echo "=== Building fuzz_mongocrypt ===" -$CC $CFLAGS -c ../test/fuzz_mongocrypt.c \ - -I../src \ - -I. \ - -o fuzz_mongocrypt.o - -$CXX $CXXFLAGS $LIB_FUZZING_ENGINE fuzz_mongocrypt.o \ - -o "$OUT/fuzz_mongocrypt" \ - libmongocrypt_static.a \ - kms-message/libkms_message_static.a \ - -Wl,--start-group \ - _mongo-c-driver/src/libbson/libbson-static-for-libmongocrypt.a \ - -Wl,--end-group - -echo "" -echo "=== Build successful! ===" -echo "" -echo "Fuzzers built:" -ls -lh "$OUT"/fuzz_* - -echo "" -echo "To run a fuzzer:" -echo " $OUT/fuzz_kms -max_total_time=60" -echo " $OUT/fuzz_mongocrypt -max_total_time=60" -echo "" -echo "=== Copying seed corpus ===" -mkdir -p "$OUT/fuzz_mongocrypt_seed_corpus" -cp ../test/data/fuzz_mongocrypt_corpus/* "$OUT/fuzz_mongocrypt_seed_corpus/" - -echo "" -echo "To run with the seed corpus:" -echo " $OUT/fuzz_mongocrypt $OUT/fuzz_mongocrypt_seed_corpus -max_total_time=60" -echo " $OUT/fuzz_kms -max_total_time=60" -