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
9 changes: 4 additions & 5 deletions tests/fuzz/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ $(TARGET_DIR)/exec: exec.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(TARGET_DIR)/json: json.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

$(TARGET_DIR)/numpy: numpy.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

# New targets
$(TARGET_DIR)/shadow_corrupt: shadow-corrupt.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@
Expand Down Expand Up @@ -115,6 +112,8 @@ $(TARGET_DIR)/ivf_cell_overflow: ivf-cell-overflow.c $(FUZZ_SRCS) | $(TARGET_DIR
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

$(TARGET_DIR)/ivf_rescore: ivf-rescore.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

$(TARGET_DIR)/diskann_operations: diskann-operations.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

Expand Down Expand Up @@ -145,7 +144,7 @@ $(TARGET_DIR)/diskann_prune_direct: diskann-prune-direct.c $(FUZZ_SRCS) | $(TARG
$(TARGET_DIR)/diskann_command_inject: diskann-command-inject.c $(FUZZ_SRCS) | $(TARGET_DIR)
$(FUZZ_CC) $(FUZZ_CFLAGS) $(FUZZ_SRCS) $< -o $@

FUZZ_TARGETS = vec0_create exec json numpy \
FUZZ_TARGETS = vec0_create exec json \
shadow_corrupt vec0_operations scalar_functions \
vec0_create_full metadata_columns vec_each vec_mismatch \
vec0_delete_completeness \
Expand All @@ -154,7 +153,7 @@ FUZZ_TARGETS = vec0_create exec json numpy \
rescore_quantize_edge rescore_interleave \
ivf_create ivf_operations \
ivf_quantize ivf_kmeans ivf_shadow_corrupt \
ivf_knn_deep ivf_cell_overflow ivf_rescore
ivf_knn_deep ivf_cell_overflow ivf_rescore \
diskann_operations diskann_create diskann_graph_corrupt \
diskann_deep_search diskann_blob_truncate \
diskann_delete_stress diskann_buffer_flush \
Expand Down
7 changes: 0 additions & 7 deletions tests/fuzz/numpy.dict

This file was deleted.

48 changes: 28 additions & 20 deletions tests/fuzz/rescore-quantize-edge.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ extern size_t _test_rescore_quantized_byte_size_int8(size_t dimensions);
* - Tests rescore_quantized_byte_size with arbitrary dimension values
* (including 0, 1, 7, MAX values -- looking for integer division issues)
* - Passes raw float reinterpretation of fuzz bytes (NaN, Inf, denormals,
* negative zero -- these are the values that break min/max/range logic)
* - Tests the int8 quantizer with all-identical values (range=0 branch)
* - Tests the int8 quantizer with extreme ranges (overflow in scale calc)
* negative zero: the values that must reach the int8 quantizer's clamps
* before its conversion to int8)
* - Tests the int8 quantizer with all-identical values (each element is
* quantized on its own, so all get the same code)
* - Tests the int8 quantizer with values far outside [-1, 1]
* - Tests bit quantizer with exact float threshold (0.0f boundary)
*/
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
Expand Down Expand Up @@ -63,10 +65,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
size_t dim = (num_floats / 8) * 8;
if (dim == 0) return 0;

const float *src = (const float *)data;
/* The fuzz bytes start one byte past the mode byte, so they are copied
out to be read as floats at float alignment. */
float *src = (float *)malloc(dim * sizeof(float));
size_t bit_bytes = dim / 8;
uint8_t *dst = (uint8_t *)malloc(bit_bytes);
if (!dst) return 0;
if (!src || !dst) { free(src); free(dst); return 0; }
memcpy(src, data, dim * sizeof(float));

_test_rescore_quantize_float_to_bit(src, dst, dim);

Expand All @@ -82,23 +87,25 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
/* NaN: comparison is false, so bit_set should be 0 */
}

free(src);
free(dst);
break;
}

case 2: {
/* Int8 quantize with raw reinterpreted floats.
The dangerous paths:
- All values identical (range == 0) -> memset path
- vmin/vmax with NaN (NaN < anything is false, NaN > anything is false)
- Extreme range causing scale = 255/range to be Inf or 0
- denormals near the clamping boundaries */
/* Int8 quantize with raw reinterpreted floats. The quantizer maps
[-1, 1] onto [-128, 127] and clamps before converting to int8, so
NaN, Inf and values far outside [-1, 1] must each reach a clamp:
converting a float outside int8's range to int8 is undefined
behavior, which UBSan reports. */
size_t num_floats = size / sizeof(float);
if (num_floats == 0) return 0;

const float *src = (const float *)data;
/* Copied out to float alignment, as in mode 1 */
float *src = (float *)malloc(num_floats * sizeof(float));
int8_t *dst = (int8_t *)malloc(num_floats);
if (!dst) return 0;
if (!src || !dst) { free(src); free(dst); return 0; }
memcpy(src, data, num_floats * sizeof(float));

_test_rescore_quantize_float_to_int8(src, dst, num_floats);

Expand All @@ -108,12 +115,13 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
assert(dst[i] >= -128 && dst[i] <= 127);
}

free(src);
free(dst);
break;
}

case 3: {
/* Int8 quantize stress: all-same values (range=0 branch) */
/* Int8 quantize stress: all-same values */
size_t dim = (size < 64) ? size : 64;
if (dim == 0) return 0;

Expand All @@ -128,9 +136,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

_test_rescore_quantize_float_to_int8(src, dst, dim);

/* All outputs should be 0 when range == 0 */
for (size_t i = 0; i < dim; i++) {
assert(dst[i] == 0);
/* Each element is quantized on its own, so all get the same code */
for (size_t i = 1; i < dim; i++) {
assert(dst[i] == dst[0]);
}

free(src);
Expand All @@ -139,9 +147,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
}

case 4: {
/* Int8 quantize with extreme range: one huge positive, one huge negative.
Tests scale = 255.0f / range overflow to Inf, then v * Inf = Inf,
then clamping to [-128, 127]. */
/* Int8 quantize with extreme values: one huge positive, one huge
negative, and the rest thousands, all far outside [-1, 1] and
clamped to [-128, 127]. */
if (size < 2 * sizeof(float)) return 0;

float extreme[2];
Expand Down