Skip to content

Commit 6d2cd3c

Browse files
author
Andreas Garnæs
committed
Add fuzzer
1 parent 6644ee4 commit 6d2cd3c

10 files changed

Lines changed: 231 additions & 0 deletions

fuzz/Makefile.fuzz

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Makefile for fuzzing roaring buffer functions without PostgreSQL deps
2+
#
3+
# Usage:
4+
# make -f Makefile.fuzz clean
5+
# make -f Makefile.fuzz
6+
# ./fuzz_buffer_reader -max_total_time=60 corpus/
7+
8+
# Configuration
9+
FUZZER_NAME = fuzz_buffer_reader
10+
CORPUS_DIR = corpus
11+
SRC_DIR = ..
12+
13+
# Try to find clang
14+
CC = $(shell which clang)
15+
16+
# Fuzzing flags
17+
FUZZ_CFLAGS = -fsanitize=fuzzer,address,undefined -g -O1 -fno-omit-frame-pointer
18+
19+
# Additional flags
20+
CFLAGS = $(FUZZ_CFLAGS) \
21+
-I$(SRC_DIR) \
22+
-std=c11 \
23+
-Wno-unused-function \
24+
-Wno-unused-variable \
25+
-Wno-missing-prototypes
26+
27+
# Source files
28+
FUZZ_SOURCES = $(FUZZER_NAME).c
29+
30+
# Build targets
31+
.PHONY: all clean corpus test quick_fuzz help
32+
33+
all: $(FUZZER_NAME)
34+
35+
# Build the fuzzer binary
36+
$(FUZZER_NAME): $(FUZZ_SOURCES)
37+
@echo "Compiling roaring buffer fuzzer..."
38+
$(CC) $(CFLAGS) -o $@ $^
39+
40+
# Test compilation without running
41+
test: $(FUZZER_NAME)
42+
@echo "✓ Roaring buffer fuzzer compiled successfully: $(FUZZER_NAME)"
43+
@echo "Run with: ./$(FUZZER_NAME) -max_total_time=60 $(CORPUS_DIR)/"
44+
45+
# Clean build artifacts
46+
clean:
47+
@rm -f $(FUZZER_NAME)
48+
@echo "Cleaned build artifacts"
49+
50+
# Quick fuzzing session (60 seconds)
51+
quick_fuzz: $(FUZZER_NAME) corpus
52+
@echo "Starting 60-second fuzzing session..."
53+
./$(FUZZER_NAME) -max_total_time=60 -print_final_stats=1 $(CORPUS_DIR)/
54+
55+
# Long fuzzing session (10 minutes)
56+
long_fuzz: $(FUZZER_NAME) corpus
57+
@echo "Starting 10-minute fuzzing session..."
58+
./$(FUZZER_NAME) -max_total_time=600 -print_final_stats=1 $(CORPUS_DIR)/
59+
60+
# Debug run with single input
61+
debug: $(FUZZER_NAME)
62+
@echo "Debug run with minimal input..."
63+
@printf '\x3a\x30\x00\x00\x00\x00\x00\x00' | ./$(FUZZER_NAME)
64+
65+
# Show help
66+
help:
67+
@echo "Available targets:"
68+
@echo " all - Build the minimal fuzzer"
69+
@echo " test - Build and test compilation"
70+
@echo " quick_fuzz - Run 60-second fuzzing session"
71+
@echo " long_fuzz - Run 10-minute fuzzing session"
72+
@echo " debug - Debug run with minimal input"
73+
@echo " clean - Clean build artifacts"
74+
@echo " help - Show this help"

fuzz/corpus/cookie_only.bin

4 Bytes
Binary file not shown.

fuzz/corpus/cookie_with_size.bin

4 Bytes
Binary file not shown.

fuzz/corpus/empty.bin

Whitespace-only changes.

fuzz/corpus/huge_size.bin

8 Bytes
Binary file not shown.

fuzz/corpus/invalid_cookie.bin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
����

fuzz/corpus/no_containers.bin

8 Bytes
Binary file not shown.

fuzz/corpus/one_element.bin

18 Bytes
Binary file not shown.

fuzz/corpus/truncated.bin

12 Bytes
Binary file not shown.

fuzz/fuzz_buffer_reader.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#include <assert.h>
2+
#include <stddef.h>
3+
#include <stdint.h>
4+
#include <stdlib.h>
5+
#include <string.h>
6+
7+
#include "../roaring.h"
8+
#include "../roaring_buffer_reader.h"
9+
10+
#include "../roaring.c"
11+
#include "../roaring_buffer_reader.c"
12+
13+
#include <inttypes.h>
14+
#include <stdio.h>
15+
16+
void CHECK_TRUE(bool b, const char *reason) {
17+
if (!b) {
18+
fprintf(stderr, "CHECK_TRUE failed: %s\n", reason);
19+
abort();
20+
}
21+
}
22+
23+
void CHECK_FALSE(bool b, const char *reason) {
24+
if (b) {
25+
fprintf(stderr, "CHECK_FALSE failed: %s\n", reason);
26+
abort();
27+
}
28+
}
29+
30+
void CHECK_EQ(uint64_t a, uint64_t b, const char *reason) {
31+
if (a != b) {
32+
fprintf(stderr, "CHECK_EQ failed: %s (%" PRIu64 " != %" PRIu64 ")\n", reason, a, b);
33+
abort();
34+
}
35+
}
36+
37+
void CHECK_DOUBLE_EQ(double a, double b, const char *reason) {
38+
if (a != b) {
39+
fprintf(stderr, "CHECK_DOUBLE_EQ failed: %s (%f != %f)\n", reason, a, b);
40+
abort();
41+
}
42+
}
43+
44+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
45+
roaring_bitmap_t *bitmap1 = roaring_bitmap_portable_deserialize_safe((const char *)data, size);
46+
if (bitmap1 == NULL) {
47+
return -1;
48+
}
49+
50+
roaring_buffer_t *buffer1 = roaring_buffer_create((const char *)data, size);
51+
CHECK_TRUE(buffer1 != NULL, "buffer1 creation");
52+
53+
CHECK_EQ(roaring_bitmap_get_cardinality(bitmap1), roaring_buffer_get_cardinality(buffer1), "cardinality");
54+
CHECK_EQ(roaring_bitmap_is_empty(bitmap1), roaring_buffer_is_empty(buffer1), "is_empty");
55+
56+
if (!roaring_bitmap_is_empty(bitmap1)) {
57+
uint32_t expected_min, actual_min;
58+
bool success1 = roaring_buffer_minimum(buffer1, &actual_min);
59+
CHECK_TRUE(success1, "buffer_minimum success");
60+
expected_min = roaring_bitmap_minimum(bitmap1);
61+
CHECK_EQ(expected_min, actual_min, "minimum value");
62+
63+
uint32_t expected_max, actual_max;
64+
bool success2 = roaring_buffer_maximum(buffer1, &actual_max);
65+
CHECK_TRUE(success2, "buffer_maximum success");
66+
expected_max = roaring_bitmap_maximum(bitmap1);
67+
CHECK_EQ(expected_max, actual_max, "maximum value");
68+
69+
for (uint32_t val = expected_min; val <= expected_max; val += (expected_max - expected_min) / 10) {
70+
bool expected_contains = roaring_bitmap_contains(bitmap1, val);
71+
bool actual_contains;
72+
bool success = roaring_buffer_contains(buffer1, val, &actual_contains);
73+
CHECK_TRUE(success, "buffer_contains success");
74+
CHECK_EQ(expected_contains, actual_contains, "contains value");
75+
76+
uint64_t expected_rank = roaring_bitmap_rank(bitmap1, val);
77+
uint64_t actual_rank;
78+
success = roaring_buffer_rank(buffer1, val, &actual_rank);
79+
CHECK_TRUE(success, "buffer_rank success");
80+
CHECK_EQ(expected_rank, actual_rank, "rank value");
81+
}
82+
}
83+
84+
85+
uint64_t card = roaring_bitmap_get_cardinality(bitmap1);
86+
87+
if (card > 1) {
88+
uint32_t *values = (uint32_t *)malloc(card * sizeof(uint32_t));
89+
roaring_bitmap_to_uint32_array(bitmap1, values);
90+
91+
size_t subset_size = card / 2;
92+
uint32_t *subset_values = (uint32_t *)malloc(subset_size * sizeof(uint32_t));
93+
for (size_t i = 0; i < subset_size; ++i) {
94+
subset_values[i] = values[i * 2];
95+
}
96+
97+
roaring_bitmap_t *bitmap2 = roaring_bitmap_of_ptr(subset_size, subset_values);
98+
char *serialized_buffer = (char *)malloc(roaring_bitmap_portable_size_in_bytes(bitmap2));
99+
size_t serialized_size = roaring_bitmap_portable_serialize(bitmap2, serialized_buffer);
100+
roaring_buffer_t *buffer2 = roaring_buffer_create(serialized_buffer, serialized_size);
101+
CHECK_TRUE(buffer2 != NULL, "buffer2 creation");
102+
103+
bool expected_equals = roaring_bitmap_equals(bitmap1, bitmap2);
104+
bool actual_equals;
105+
bool success = roaring_buffer_equals(buffer1, buffer2, &actual_equals);
106+
CHECK_TRUE(success, "buffer_equals success");
107+
CHECK_EQ(expected_equals, actual_equals, "equals");
108+
109+
110+
bool expected_is_subset = roaring_bitmap_is_subset(bitmap1, bitmap2);
111+
bool actual_is_subset;
112+
success = roaring_buffer_is_subset(buffer1, buffer2, &actual_is_subset);
113+
CHECK_TRUE(success, "buffer_is_subset success");
114+
CHECK_EQ(expected_is_subset, actual_is_subset, "is_subset");
115+
116+
uint64_t and_card;
117+
success = roaring_buffer_and_cardinality(buffer1, buffer2, &and_card);
118+
CHECK_TRUE(success, "buffer_and_cardinality success");
119+
CHECK_EQ(roaring_bitmap_and_cardinality(bitmap1, bitmap2), and_card, "and_cardinality");
120+
121+
uint64_t or_card;
122+
success = roaring_buffer_or_cardinality(buffer1, buffer2, &or_card);
123+
CHECK_TRUE(success, "buffer_or_cardinality success");
124+
CHECK_EQ(roaring_bitmap_or_cardinality(bitmap1, bitmap2), or_card, "or_cardinality");
125+
126+
uint64_t xor_card;
127+
success = roaring_buffer_xor_cardinality(buffer1, buffer2, &xor_card);
128+
CHECK_TRUE(success, "buffer_xor_cardinality success");
129+
CHECK_EQ(roaring_bitmap_xor_cardinality(bitmap1, bitmap2), xor_card, "xor_cardinality");
130+
131+
uint64_t andnot_card;
132+
success = roaring_buffer_andnot_cardinality(buffer1, buffer2, &andnot_card);
133+
CHECK_TRUE(success, "buffer_andnot_cardinality success");
134+
CHECK_EQ(roaring_bitmap_andnot_cardinality(bitmap1, bitmap2), andnot_card, "andnot_cardinality");
135+
136+
double jaccard;
137+
success = roaring_buffer_jaccard_index(buffer1, buffer2, &jaccard);
138+
CHECK_TRUE(success, "buffer_jaccard_index success");
139+
CHECK_DOUBLE_EQ(roaring_bitmap_jaccard_index(bitmap1, bitmap2), jaccard, "jaccard_index");
140+
141+
bool intersect;
142+
success = roaring_buffer_intersect(buffer1, buffer2, &intersect);
143+
CHECK_TRUE(success, "buffer_intersect success");
144+
CHECK_EQ(roaring_bitmap_intersect(bitmap1, bitmap2), intersect, "intersect");
145+
146+
roaring_bitmap_free(bitmap2);
147+
roaring_buffer_free(buffer2);
148+
free(serialized_buffer);
149+
free(values);
150+
free(subset_values);
151+
}
152+
153+
roaring_bitmap_free(bitmap1);
154+
roaring_buffer_free(buffer1);
155+
return 0;
156+
}

0 commit comments

Comments
 (0)