sqlite-vec/tests/fuzz/scalar-functions.c
Alex Garcia a61d45183b Add comprehensive fuzz testing infrastructure with 6 new targets
- Fix numpy.c: tautology bug (|| → &&), infinite loop, and missing
  sqlite3_vec_numpy_init call
- Replace tests/fuzz/Makefile: auto-detect clang, add UBSAN, macOS
  ld_classic workaround, generic build rules for all 10 targets
- Add 6 new fuzz targets: shadow-corrupt (corrupted shadow tables),
  vec0-operations (INSERT/DELETE/query sequences), scalar-functions
  (all 18 SQL scalar functions), vec0-create-full (CREATE + lifecycle),
  metadata-columns (metadata/auxiliary columns), vec-each (vec_each TVF)
- Add seed corpora for shadow-corrupt, vec0-operations, exec, and json
- Add fuzz-build/fuzz-quick/fuzz-long targets to root Makefile

All 10 targets verified building and running on macOS ARM (Apple Silicon).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 20:33:05 -08:00

81 lines
2.8 KiB
C

#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sqlite-vec.h"
#include "sqlite3.h"
#include <assert.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 2) return 0;
int rc;
sqlite3 *db;
sqlite3_stmt *stmt = NULL;
rc = sqlite3_open(":memory:", &db);
assert(rc == SQLITE_OK);
rc = sqlite3_vec_init(db, NULL, NULL);
assert(rc == SQLITE_OK);
static const char *queries[] = {
"SELECT vec_f32(cast(? as text))", /* 0: JSON text -> f32 */
"SELECT vec_f32(?)", /* 1: blob -> f32 */
"SELECT vec_int8(?)", /* 2: blob -> int8 */
"SELECT vec_bit(?)", /* 3: blob -> bit */
"SELECT vec_length(?)", /* 4: vector length */
"SELECT vec_type(?)", /* 5: vector type string */
"SELECT vec_to_json(?)", /* 6: vector -> JSON */
"SELECT vec_normalize(?)", /* 7: normalize */
"SELECT vec_quantize_binary(?)", /* 8: quantize to binary */
"SELECT vec_quantize_int8(?, 'unit')", /* 9: quantize to int8 */
"SELECT vec_distance_l2(?, ?)", /* 10: L2 distance */
"SELECT vec_distance_cosine(?, ?)", /* 11: cosine distance */
"SELECT vec_distance_l1(?, ?)", /* 12: L1 distance */
"SELECT vec_distance_hamming(?, ?)", /* 13: hamming distance */
"SELECT vec_add(?, ?)", /* 14: vector add */
"SELECT vec_sub(?, ?)", /* 15: vector subtract */
"SELECT vec_slice(?, 0, ?)", /* 16: vector slice */
};
static const int nQueries = sizeof(queries) / sizeof(queries[0]);
int qIdx = data[0] % nQueries;
const uint8_t *payload = data + 1;
int payload_size = (int)(size - 1);
rc = sqlite3_prepare_v2(db, queries[qIdx], -1, &stmt, NULL);
if (rc != SQLITE_OK) {
sqlite3_close(db);
return 0;
}
int nParams = sqlite3_bind_parameter_count(stmt);
// Bind param 1: fuzz payload as blob
sqlite3_bind_blob(stmt, 1, payload, payload_size, SQLITE_STATIC);
if (nParams >= 2) {
if (qIdx == 16) {
// vec_slice 3rd param is integer (end index)
int end_idx = (payload_size > 0) ? (payload[0] % 64) : 0;
sqlite3_bind_int(stmt, 2, end_idx);
} else {
// For 2-param functions (distance, add, sub): split payload in half
int half = payload_size / 2;
sqlite3_bind_blob(stmt, 2, payload + half,
payload_size - half, SQLITE_STATIC);
}
}
if (nParams >= 3) {
// vec_slice: param 3 is the end index
int end_idx = (payload_size > 1) ? (payload[1] % 64) : 0;
sqlite3_bind_int(stmt, 3, end_idx);
}
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}