Raise IndexError for oversized SQLite blob indices (#9048)

Use index-sized conversion, preserve __index__ exceptions, and bound native type names to the UTF-8-safe diagnostic limit. Include in-memory blob read and write regressions without changing callback ownership.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-12 01:10:08 +09:00
1 parent 37fdf4d2d9
commit 4638b6a5f3
2 files changed
+79 -7

No files matched your search

+23 -7
View File
@@ -2839,15 +2839,31 @@ mod _sqlite3 {
}
}
fn wrapped_index(index: PyIntRef, length: c_int, vm: &VirtualMachine) -> PyResult<c_int> {
let mut index = index.try_to_primitive::<c_int>(vm)?;
fn wrapped_index(
index: PyIntRef,
object: &PyObject,
length: c_int,
vm: &VirtualMachine,
) -> PyResult<c_int> {
let mut index = index.try_to_primitive::<isize>(vm).map_err(|_| {
let class = object.class();
let type_name = class.slot_name();
let mut end = type_name.len().min(200);
while !type_name.is_char_boundary(end) {
end -= 1;
}
vm.new_index_error(format!(
"cannot fit '{}' into an index-sized integer",
&type_name[..end]
))
})?;
if index < 0 {
index += length;
index += length as isize;
}
if index < 0 || index >= length {
if index < 0 || index >= length as isize {
Err(vm.new_index_error("Blob index out of range"))
} else {
Ok(index)
Ok(index as c_int)
}
}
@@ -2874,7 +2890,7 @@ mod _sqlite3 {
let inner = self.inner(vm)?;
if let Some(index) = needle.try_index_opt(vm) {
let blob_len = inner.blob.bytes();
let index = Self::wrapped_index(index?, blob_len, vm)?;
let index = Self::wrapped_index(index?, needle, blob_len, vm)?;
let mut byte: u8 = 0;
let ret = inner.blob.read_single(&mut byte, index);
self.check(ret, vm).map(|_| vm.ctx.new_int(byte).into())
@@ -2937,7 +2953,7 @@ mod _sqlite3 {
)));
};
let blob_len = inner.blob.bytes();
let index = Self::wrapped_index(index?, blob_len, vm)?;
let index = Self::wrapped_index(index?, needle, blob_len, vm)?;
// Mirror CPython ass_subscript_index: use PyLong_AsLong, treat any
// overflow (e.g. 2**65) as -1, then validate the [0, 255] range.
let val = int_val.as_bigint().to_i64().unwrap_or(-1);
+56
View File
@@ -306,3 +306,59 @@ for operation in (
"replace_callback",
):
check_nested_aggregate_destructor(operation)
class BlobIndex:
def __init__(self, value):
self.value = value
def __index__(self):
return self.value
class BlobInt(int):
pass
class BrokenBlobIndex:
def __index__(self):
raise OverflowError("index conversion failed")
cx.execute("CREATE TABLE blobindices(b BLOB)")
cx.execute("INSERT INTO blobindices(b) VALUES (?)", (b"abc",))
with cx.blobopen("blobindices", "b", 1) as blob:
assert blob[BlobIndex(-1)] == ord("c")
blob[BlobIndex(0)] = ord("A")
assert blob[0] == ord("A")
test = unittest.TestCase()
for index in (sys.maxsize, -sys.maxsize - 1):
with test.assertRaisesRegex(IndexError, "^Blob index out of range$"):
blob[index]
with test.assertRaisesRegex(IndexError, "^Blob index out of range$"):
blob[index] = 0
for value in (sys.maxsize + 1, -sys.maxsize - 2):
for index in (value, BlobInt(value), BlobIndex(value)):
message = (
f"^cannot fit '{type(index).__name__}' into an index-sized integer$"
)
with test.assertRaisesRegex(IndexError, message):
blob[index]
with test.assertRaisesRegex(IndexError, message):
blob[index] = 0
for name, bounded_name in (("X" * 250, "X" * 200), ("€" * 67, "€" * 66)):
long_index = type(name, (), {"__index__": lambda self: sys.maxsize + 1})()
message = f"^cannot fit '{bounded_name}' into an index-sized integer$"
with test.assertRaisesRegex(IndexError, message):
blob[long_index]
with test.assertRaisesRegex(IndexError, message):
blob[long_index] = 0
with test.assertRaisesRegex(OverflowError, "^index conversion failed$"):
blob[BrokenBlobIndex()]
with test.assertRaisesRegex(OverflowError, "^index conversion failed$"):
blob[BrokenBlobIndex()] = 0
assert blob[:] == b"Abc"