Skip to content

Commit 623661e

Browse files
committed
Pass correct pointer to cleanup in ensure_vector_match error path
When the second vector fails to parse in ensure_vector_match(), the cleanup function for the first vector was called with 'a' (void**) instead of '*a' (void*). This caused sqlite3_free to be called with a stack address instead of the heap-allocated vector, resulting in a crash: malloc: Non-aligned pointer being freed Fatal error 6: Aborted The fix dereferences the pointer correctly, matching how cleanup is done in other error paths. This fix has a unit test that will crash without the patch.
1 parent 9facf1a commit 623661e

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

sqlite-vec.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ int ensure_vector_match(sqlite3_value *aValue, sqlite3_value *bValue, void **a,
11371137
if (rc != SQLITE_OK) {
11381138
*outError = sqlite3_mprintf("Error reading 2nd vector: %s", error);
11391139
sqlite3_free(error);
1140-
aCleanup(a);
1140+
aCleanup(*a);
11411141
return SQLITE_ERROR;
11421142
}
11431143

tests/test-loadable.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,32 @@ def check(a, b, dtype=np.float32):
443443
abs_tol=1e-6
444444
)
445445

446+
def test_ensure_vector_match_cleanup_on_second_vector_error():
447+
"""
448+
Test that ensure_vector_match properly cleans up the first vector
449+
when the second vector fails to parse.
450+
451+
This tests the fix for a bug where aCleanup(a) was called instead of
452+
aCleanup(*a), passing the wrong pointer to the cleanup function.
453+
454+
The bug only manifests when the first vector is parsed from JSON/TEXT
455+
(which uses sqlite3_free as cleanup) rather than BLOB (which uses noop).
456+
"""
457+
# Valid first vector as JSON text - this causes memory allocation
458+
# and sets cleanup to sqlite3_free
459+
valid_vector_json = "[1.0, 2.0, 3.0, 4.0]"
460+
461+
# Invalid second vector: 5 bytes, not divisible by 4 (sizeof float32)
462+
# This will fail in fvec_from_value with "invalid float32 vector BLOB length"
463+
invalid_vector = b"\x01\x02\x03\x04\x05"
464+
465+
with pytest.raises(sqlite3.OperationalError, match=r"^Error reading 2nd vector: invalid float32 vector BLOB length\. Must be divisible by 4, found 5$"):
466+
db.execute(
467+
"select vec_distance_cosine(?, ?)",
468+
[valid_vector_json, invalid_vector]
469+
).fetchone()
470+
471+
446472
def test_vec_distance_hamming():
447473
vec_distance_hamming = lambda *args: db.execute(
448474
"select vec_distance_hamming(vec_bit(?), vec_bit(?))", args

0 commit comments

Comments
 (0)