From 4bee88384bf6339d4794da1e6644aaca29a5678f Mon Sep 17 00:00:00 2001 From: Alex Garcia Date: Tue, 31 Mar 2026 14:51:27 -0700 Subject: [PATCH] Reject IVF binary quantizer when dimensions not divisible by 8 The binary quantizer uses D/8 for buffer sizes and memset, which truncates for non-multiple-of-8 dimensions, causing OOB writes. Rather than using ceiling division, enforce the constraint at table creation time with a clear parse error. Co-Authored-By: Claude Opus 4.6 (1M context) --- sqlite-vec.c | 3 +++ tests/test-ivf-quantization.py | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/sqlite-vec.c b/sqlite-vec.c index 53c4635..d12e25d 100644 --- a/sqlite-vec.c +++ b/sqlite-vec.c @@ -3074,6 +3074,9 @@ int vec0_parse_vector_column(const char *source, int source_length, if (rc != SQLITE_OK) { return SQLITE_ERROR; } + if (ivfConfig.quantizer == VEC0_IVF_QUANTIZER_BINARY && (dimensions % 8) != 0) { + return SQLITE_ERROR; + } #else return SQLITE_ERROR; // IVF not compiled in #endif diff --git a/tests/test-ivf-quantization.py b/tests/test-ivf-quantization.py index 9790680..b4d6ae3 100644 --- a/tests/test-ivf-quantization.py +++ b/tests/test-ivf-quantization.py @@ -253,3 +253,20 @@ def test_ivf_quantized_delete(db): db.execute("DELETE FROM t WHERE rowid = 5") # _ivf_vectors should have 9 rows assert db.execute("SELECT count(*) FROM t_ivf_vectors00").fetchone()[0] == 9 + + +def test_ivf_binary_rejects_non_multiple_of_8_dims(db): + """Binary quantizer requires dimensions divisible by 8.""" + with pytest.raises(sqlite3.OperationalError): + db.execute( + "CREATE VIRTUAL TABLE t USING vec0(" + " v float[12] indexed by ivf(quantizer=binary)" + ")" + ) + + # Dimensions divisible by 8 should work + db.execute( + "CREATE VIRTUAL TABLE t2 USING vec0(" + " v float[16] indexed by ivf(quantizer=binary)" + ")" + )