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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions fuzz/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Makefile for fuzzing roaring buffer functions without PostgreSQL deps
#
# Usage:
# make clean
# make
# ./fuzz_buffer_reader -max_total_time=60 corpus/

# Configuration
FUZZER_NAME = fuzz_buffer_reader
CORPUS_DIR = corpus
SRC_DIR = ..

# Try to find clang
CC ?= $(shell which clang)

# Fuzzing flags
FUZZ_CFLAGS = -fsanitize=fuzzer,address,undefined -g -O1 -fno-omit-frame-pointer

# Additional flags
CFLAGS = $(FUZZ_CFLAGS) \
-I$(SRC_DIR) \
-std=c11 \
-Wno-unused-function \
-Wno-unused-variable \
-Wno-missing-prototypes

# Source files
FUZZ_SOURCES = $(FUZZER_NAME).c

# Build targets
.PHONY: all clean corpus test quick_fuzz help

all: $(FUZZER_NAME)

# Build the fuzzer binary
$(FUZZER_NAME): $(FUZZ_SOURCES)
@echo "Compiling roaring buffer fuzzer..."
$(CC) $(CFLAGS) -o $@ $^

# Test compilation without running
test: $(FUZZER_NAME)
@echo "✓ Roaring buffer fuzzer compiled successfully: $(FUZZER_NAME)"
@echo "Run with: ./$(FUZZER_NAME) -max_total_time=60 $(CORPUS_DIR)/"

# Clean build artifacts
clean:
@rm -f $(FUZZER_NAME)
@echo "Cleaned build artifacts"

# Quick fuzzing session (60 seconds)
quick_fuzz: $(FUZZER_NAME) corpus
@echo "Starting 60-second fuzzing session..."
./$(FUZZER_NAME) -max_total_time=60 -print_final_stats=1 $(CORPUS_DIR)/

# Long fuzzing session (10 minutes)
long_fuzz: $(FUZZER_NAME) corpus
@echo "Starting 10-minute fuzzing session..."
./$(FUZZER_NAME) -max_total_time=600 -print_final_stats=1 $(CORPUS_DIR)/

# Debug run with single input
debug: $(FUZZER_NAME)
@echo "Debug run with minimal input..."
@printf '\x3a\x30\x00\x00\x00\x00\x00\x00' | ./$(FUZZER_NAME)

# Show help
help:
@echo "Available targets:"
@echo " all - Build the minimal fuzzer"
@echo " test - Build and test compilation"
@echo " quick_fuzz - Run 60-second fuzzing session"
@echo " long_fuzz - Run 10-minute fuzzing session"
@echo " debug - Debug run with minimal input"
@echo " clean - Clean build artifacts"
@echo " help - Show this help"
Binary file added fuzz/corpus/cookie_only.bin
Binary file not shown.
Binary file added fuzz/corpus/cookie_with_size.bin
Binary file not shown.
Empty file added fuzz/corpus/empty.bin
Empty file.
Binary file added fuzz/corpus/huge_size.bin
Binary file not shown.
1 change: 1 addition & 0 deletions fuzz/corpus/invalid_cookie.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
����
Binary file added fuzz/corpus/no_containers.bin
Binary file not shown.
Binary file added fuzz/corpus/one_element.bin
Binary file not shown.
Binary file added fuzz/corpus/truncated.bin
Binary file not shown.
161 changes: 161 additions & 0 deletions fuzz/fuzz_buffer_reader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "../roaring.h"
#include "../roaring_buffer_reader.h"

#include "../roaring.c"
#include "../roaring_buffer_reader.c"

#include <inttypes.h>
#include <stdio.h>

void CHECK_TRUE(bool b, const char *reason) {
if (!b) {
fprintf(stderr, "CHECK_TRUE failed: %s\n", reason);
abort();
}
}

void CHECK_FALSE(bool b, const char *reason) {
if (b) {
fprintf(stderr, "CHECK_FALSE failed: %s\n", reason);
abort();
}
}

void CHECK_EQ(uint64_t a, uint64_t b, const char *reason) {
if (a != b) {
fprintf(stderr, "CHECK_EQ failed: %s (%" PRIu64 " != %" PRIu64 ")\n", reason, a, b);
abort();
}
}

void CHECK_DOUBLE_EQ(double a, double b, const char *reason) {
if (a != b) {
fprintf(stderr, "CHECK_DOUBLE_EQ failed: %s (%f != %f)\n", reason, a, b);
abort();
}
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
roaring_bitmap_t *bitmap1 = roaring_bitmap_portable_deserialize_safe((const char *)data, size);
if (bitmap1 == NULL) {
return -1;
}

const char *reason;
if (!roaring_bitmap_internal_validate(bitmap1, &reason)) {
roaring_bitmap_free(bitmap1);
return -1;
}

char *serialized_buffer1 = (char *)malloc(roaring_bitmap_portable_size_in_bytes(bitmap1));
size_t serialized_size1 = roaring_bitmap_portable_serialize(bitmap1, serialized_buffer1);

roaring_buffer_t *buffer1 = roaring_buffer_create((const char *)serialized_buffer1, serialized_size1);
CHECK_TRUE(buffer1 != NULL, "buffer1 creation");

uint64_t expected_cardinality = roaring_bitmap_get_cardinality(bitmap1);
uint64_t actual_cardinality = roaring_buffer_get_cardinality(buffer1);
CHECK_EQ(expected_cardinality, actual_cardinality, "cardinality");
CHECK_EQ(roaring_bitmap_is_empty(bitmap1), roaring_buffer_is_empty(buffer1), "is_empty");

uint32_t expected_min, actual_min;
bool success1 = roaring_buffer_minimum(buffer1, &actual_min);
CHECK_TRUE(success1, "buffer_minimum success");
expected_min = roaring_bitmap_minimum(bitmap1);
CHECK_EQ(expected_min, actual_min, "minimum value");

uint32_t expected_max, actual_max;
bool success2 = roaring_buffer_maximum(buffer1, &actual_max);
CHECK_TRUE(success2, "buffer_maximum success");
expected_max = roaring_bitmap_maximum(bitmap1);
CHECK_EQ(expected_max, actual_max, "maximum value");

if (expected_cardinality > 0) {
uint32_t *values = (uint32_t *)malloc(expected_cardinality * sizeof(uint32_t));
roaring_bitmap_to_uint32_array(bitmap1, values);

for(uint32_t i = 0; i < expected_cardinality; i += 10) {
bool actual_contains;
bool success = roaring_buffer_contains(buffer1, values[i], &actual_contains);
CHECK_TRUE(success, "buffer_contains success");
CHECK_TRUE(actual_contains, "contains value");

uint64_t expected_rank = roaring_bitmap_rank(bitmap1, values[i]);
uint64_t actual_rank;
success = roaring_buffer_rank(buffer1, values[i], &actual_rank);
CHECK_TRUE(success, "buffer_rank success");
CHECK_EQ(expected_rank, actual_rank, "rank value");
}

size_t subset_size = expected_cardinality / 2;
uint32_t *subset_values = (uint32_t *)malloc(subset_size * sizeof(uint32_t));
for (size_t i = 0; i < subset_size; ++i) {
subset_values[i] = values[i * 2];
}

roaring_bitmap_t *bitmap2 = roaring_bitmap_of_ptr(subset_size, subset_values);
char *serialized_buffer2 = (char *)malloc(roaring_bitmap_portable_size_in_bytes(bitmap2));
size_t serialized_size2 = roaring_bitmap_portable_serialize(bitmap2, serialized_buffer2);
roaring_buffer_t *buffer2 = roaring_buffer_create(serialized_buffer2, serialized_size2);
CHECK_TRUE(buffer2 != NULL, "buffer2 creation");

bool expected_equals = roaring_bitmap_equals(bitmap1, bitmap2);
bool actual_equals;
bool success = roaring_buffer_equals(buffer1, buffer2, &actual_equals);
CHECK_TRUE(success, "buffer_equals success");
CHECK_EQ(expected_equals, actual_equals, "equals");

bool expected_is_subset = roaring_bitmap_is_subset(bitmap1, bitmap2);
bool actual_is_subset;
success = roaring_buffer_is_subset(buffer1, buffer2, &actual_is_subset);
CHECK_TRUE(success, "buffer_is_subset success");
CHECK_EQ(expected_is_subset, actual_is_subset, "is_subset");

uint64_t and_card;
success = roaring_buffer_and_cardinality(buffer1, buffer2, &and_card);
CHECK_TRUE(success, "buffer_and_cardinality success");
CHECK_EQ(roaring_bitmap_and_cardinality(bitmap1, bitmap2), and_card, "and_cardinality");

uint64_t or_card;
success = roaring_buffer_or_cardinality(buffer1, buffer2, &or_card);
CHECK_TRUE(success, "buffer_or_cardinality success");
CHECK_EQ(roaring_bitmap_or_cardinality(bitmap1, bitmap2), or_card, "or_cardinality");

uint64_t xor_card;
success = roaring_buffer_xor_cardinality(buffer1, buffer2, &xor_card);
CHECK_TRUE(success, "buffer_xor_cardinality success");
CHECK_EQ(roaring_bitmap_xor_cardinality(bitmap1, bitmap2), xor_card, "xor_cardinality");

uint64_t andnot_card;
success = roaring_buffer_andnot_cardinality(buffer1, buffer2, &andnot_card);
CHECK_TRUE(success, "buffer_andnot_cardinality success");
CHECK_EQ(roaring_bitmap_andnot_cardinality(bitmap1, bitmap2), andnot_card, "andnot_cardinality");

double jaccard;
success = roaring_buffer_jaccard_index(buffer1, buffer2, &jaccard);
CHECK_TRUE(success, "buffer_jaccard_index success");
CHECK_DOUBLE_EQ(roaring_bitmap_jaccard_index(bitmap1, bitmap2), jaccard, "jaccard_index");

bool intersect;
success = roaring_buffer_intersect(buffer1, buffer2, &intersect);
CHECK_TRUE(success, "buffer_intersect success");
CHECK_EQ(roaring_bitmap_intersect(bitmap1, bitmap2), intersect, "intersect");

roaring_bitmap_free(bitmap2);
roaring_buffer_free(buffer2);
free(serialized_buffer2);
free(values);
free(subset_values);
}

roaring_bitmap_free(bitmap1);
roaring_buffer_free(buffer1);
free(serialized_buffer1);
return 0;
}