Skip to content

Commit ff137e2

Browse files
Enforce file-content alignment on FilesResponse decode
The encoder already rejects non-8-byte-aligned content because upstream gateway.c::dumpFile asserts ``len % 8 == 0`` for every file. The decoder was accepting any size, so a peer that emitted bytes a real C server would never produce silently round-tripped. Mirror the encode-side invariant on decode too. Replace the old "no padding between files" test's deliberately misaligned example with an aligned one — non-aligned sizes are not realistic (SQLite pages are always multiples of 512). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a0a1cf8 commit ff137e2

2 files changed

Lines changed: 38 additions & 9 deletions

File tree

src/dqlitewire/messages/responses.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,15 @@ def decode_body(cls, data: bytes, schema: int = 0) -> "FilesResponse":
532532
offset += consumed
533533
size = decode_uint64(view[offset:])
534534
offset += 8
535+
# Mirror of the encode-side invariant: upstream
536+
# gateway.c::dumpFile asserts ``len % 8 == 0`` for every
537+
# file's content. Reject non-aligned payloads on decode too
538+
# so a mock / malicious peer cannot produce bytes the real
539+
# C server would not emit.
540+
if size % 8 != 0:
541+
raise DecodeError(
542+
f"FilesResponse content for {name!r} must be 8-byte aligned (got {size} bytes)"
543+
)
535544
if offset + size > len(view):
536545
raise DecodeError(
537546
f"FilesResponse file content truncated: expected {size} bytes "

tests/test_messages_responses.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,21 +1082,41 @@ def test_aligned_content_has_no_padding(self) -> None:
10821082
# count(8) + name "a.db\0"(8) + size(8) + content(16) = 40
10831083
assert len(body) == 40
10841084

1085-
def test_no_padding_after_content_matches_go(self) -> None:
1086-
"""Go reads file content without padding; encoder must not add padding."""
1085+
def test_no_padding_between_files_matches_go(self) -> None:
1086+
"""Files are written back-to-back with no padding between
1087+
entries (upstream gateway.c::dumpFile). Each file's own content
1088+
must be 8-byte aligned, which matches reality (SQLite pages are
1089+
always multiples of 512).
1090+
"""
10871091
from dqlitewire.types import encode_text, encode_uint64
10881092

1089-
# Manually build Go-format body: no padding after non-aligned content
10901093
body = encode_uint64(2) # count=2
10911094
body += encode_text("f1") # filename
1092-
body += encode_uint64(3) # size=3
1093-
body += b"\x01\x02\x03" # content (not word-aligned, no padding!)
1095+
body += encode_uint64(8) # size=8 (one word)
1096+
body += b"\x01\x02\x03\x04\x05\x06\x07\x08" # aligned content
10941097
body += encode_text("f2") # next filename immediately after
1095-
body += encode_uint64(1) # size=1
1096-
body += b"\xff" # content
1098+
body += encode_uint64(8) # size=8
1099+
body += b"\xff" * 8 # content
10971100
decoded = FilesResponse.decode_body(body)
1098-
assert decoded.files["f1"] == b"\x01\x02\x03"
1099-
assert decoded.files["f2"] == b"\xff"
1101+
assert decoded.files["f1"] == b"\x01\x02\x03\x04\x05\x06\x07\x08"
1102+
assert decoded.files["f2"] == b"\xff" * 8
1103+
1104+
def test_decode_rejects_non_aligned_content(self) -> None:
1105+
"""Symmetric with the encode-side alignment check: a peer that
1106+
claims a non-multiple-of-8 content size is producing bytes the
1107+
real C server could never emit.
1108+
"""
1109+
import pytest
1110+
1111+
from dqlitewire.exceptions import DecodeError
1112+
from dqlitewire.types import encode_text, encode_uint64
1113+
1114+
body = encode_uint64(1) # count=1
1115+
body += encode_text("f1")
1116+
body += encode_uint64(3) # size=3, non-aligned
1117+
body += b"\x01\x02\x03"
1118+
with pytest.raises(DecodeError, match="8-byte aligned"):
1119+
FilesResponse.decode_body(body)
11001120

11011121
def test_bogus_file_count_raises(self) -> None:
11021122
"""A file count larger than remaining data should raise DecodeError early."""

0 commit comments

Comments
 (0)