diff --git a/dissect/database/sqlite3/sqlite3.py b/dissect/database/sqlite3/sqlite3.py index e009053..ccdb0e4 100644 --- a/dissect/database/sqlite3/sqlite3.py +++ b/dissect/database/sqlite3/sqlite3.py @@ -512,7 +512,6 @@ def data(self) -> bytes: if not self._data: offset = self._offset + self._record_offset page_data = self.page.data - page_size = self.page.sqlite.page_size if self.size <= self.max_payload_size: size = max(self.size, 4) @@ -545,7 +544,7 @@ def data(self) -> bytes: # overflow_size is the size of the page data without the # extra 4 bytes for the next overflow page, so it needs to # be added. - data_size = min(overflow_size + 4, page_size) + data_size = min(overflow_size + 4, self.page.sqlite.usable_page_size) page_buf = self.page.sqlite.raw_page(overflow_page)[:data_size] overflow_page = c_sqlite3.uint32(page_buf[:4]) diff --git a/tests/_data/sqlite3/overflow.db b/tests/_data/sqlite3/overflow.db new file mode 100644 index 0000000..e9ee293 --- /dev/null +++ b/tests/_data/sqlite3/overflow.db @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4e02f0db7d5dd90f2da8d06052214d5cac84f8400d54aa13ad8d36e53f2f973c +size 20480 diff --git a/tests/sqlite3/test_sqlite3.py b/tests/sqlite3/test_sqlite3.py index 23cecbb..c2afe8f 100644 --- a/tests/sqlite3/test_sqlite3.py +++ b/tests/sqlite3/test_sqlite3.py @@ -6,6 +6,7 @@ import pytest from dissect.database.sqlite3 import sqlite3 +from tests._util import absolute_path if TYPE_CHECKING: from pathlib import Path @@ -93,3 +94,36 @@ def test_empty(empty_db: BinaryIO) -> None: assert s.encoding == "utf-8" assert len(list(s.tables())) == 0 + + +def test_cell_overflow_reserved_page_size_regression() -> None: + """Test if we handle databases with reserve_bytes greater than 0 correctly. + + This test case emulates a database with a page size of 4kb and with reserve_bytes set to 32. + We then commit a row to a dummy table with a value of 8kb, forcing a cell overflow to a new page. + + Test data generated using: + + $ sqlite3 example.db + SQLite version 3.45.1 2024-01-30 16:01:20 + Enter ".help" for usage hints. + sqlite> .filectrl reserve_bytes 32 + 32 + sqlite> VACUUM; + sqlite> CREATE TABLE foo ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "text" TEXT NOT NULL); + sqlite> .quit + + $ python + >>> import sqlite3 + >>> con = sqlite3.connect("example.db") + ... cur = con.cursor() + >>> cur.execute("INSERT INTO foo VALUES (1, ?)", ("A" * 8192,)) + >>> con.commit() + ... con.close() + """ + + db = sqlite3.SQLite3(absolute_path("_data/sqlite3/overflow.db")) + assert db.header.reserved_size == 32 + assert db.header.page_size == 4096 + assert db.usable_page_size == db.header.page_size - db.header.reserved_size + assert db.table("foo").row(0).text == "A" * 8192