From d371e20bdf9a8870492e743043dda4abb6612c8b Mon Sep 17 00:00:00 2001 From: TIM ANDERSON Date: Fri, 23 Oct 2026 13:23:59 +1000 Subject: [PATCH 1/2] fix(blf): reject objects smaller than their base header A zero-sized object leaves the next parser offset unchanged, so iteration can repeatedly process the same record. Raise BLFParseError before reading a payload when an object cannot contain its 16-byte base header. Add regressions for sizes 0, 1 and 15, plus a valid message split across log containers. Include a changelog fragment for issue #1584. AI-assisted implementation and verification. --- can/io/blf.py | 2 ++ doc/changelog.d/1584.fixed.rst | 3 +++ test/logformats_test.py | 36 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 doc/changelog.d/1584.fixed.rst diff --git a/can/io/blf.py b/can/io/blf.py index 77bd02fae..16e2ff630 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -250,6 +250,8 @@ def _parse_data(self, data: bytes) -> Iterator[Message]: signature, header_size, header_version, obj_size, obj_type = header if signature != b"LOBJ": raise BLFParseError() + if obj_size < obj_header_base_size: + raise BLFParseError(f"Invalid object size {obj_size}") # Calculate position of next object next_pos = pos + obj_size diff --git a/doc/changelog.d/1584.fixed.rst b/doc/changelog.d/1584.fixed.rst new file mode 100644 index 000000000..a5c7f3c07 --- /dev/null +++ b/doc/changelog.d/1584.fixed.rst @@ -0,0 +1,3 @@ +Raise :class:`~can.io.blf.BLFParseError` for objects inside BLF log containers +that are smaller than their base header instead of repeatedly reading a +zero-sized object. diff --git a/test/logformats_test.py b/test/logformats_test.py index 42ed29b13..9a249e4d9 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -20,6 +20,7 @@ from abc import ABCMeta, abstractmethod from contextlib import contextmanager from datetime import datetime, timedelta, timezone +from io import BytesIO from itertools import zip_longest from pathlib import Path from unittest.mock import patch @@ -849,6 +850,41 @@ def _read_log_file(self, filename): with can.BLFReader(logfile) as reader: return list(reader) + @parameterized.expand([0, 1, blf.OBJ_HEADER_BASE_STRUCT.size - 1]) + def test_invalid_object_size(self, obj_size): + with can.BLFWriter(self.test_file_name, compression_level=0) as writer: + writer(can.Message(arbitration_id=0x123, data=[1, 2, 3])) + + data = bytearray(Path(self.test_file_name).read_bytes()) + # The uncompressed container starts immediately after its two headers. + offset = ( + blf.FILE_HEADER_SIZE + + blf.OBJ_HEADER_BASE_STRUCT.size + + blf.LOG_CONTAINER_STRUCT.size + ) + header = list(blf.OBJ_HEADER_BASE_STRUCT.unpack_from(data, offset)) + header[3] = obj_size + blf.OBJ_HEADER_BASE_STRUCT.pack_into(data, offset, *header) + + with can.BLFReader(BytesIO(data)) as reader: + with self.assertRaisesRegex( + blf.BLFParseError, f"Invalid object size {obj_size}" + ): + next(iter(reader)) + + def test_object_split_between_containers(self): + messages = [ + can.Message(timestamp=1700000000, arbitration_id=0x123, data=[1, 2, 3]), + can.Message(timestamp=1700000001, arbitration_id=0x456, data=[4, 5, 6]), + ] + with can.BLFWriter(self.test_file_name, compression_level=0) as writer: + writer.max_container_size = 32 + for message in messages: + writer(message) + + with can.BLFReader(self.test_file_name) as reader: + self.assertMessagesEqual(messages, list(reader)) + def test_can_message(self): expected = can.Message( timestamp=2459565876.494607, From 3e3bad83896a86558a113d8537d7cae4ee0d8cb8 Mon Sep 17 00:00:00 2001 From: TIM ANDERSON Date: Fri, 23 Oct 2026 13:25:54 +1000 Subject: [PATCH 2/2] docs: use a literal for the internal BLF parse error Avoid an unresolved Sphinx class reference in the changelog fragment. --- doc/changelog.d/1584.fixed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.d/1584.fixed.rst b/doc/changelog.d/1584.fixed.rst index a5c7f3c07..8b7f2acce 100644 --- a/doc/changelog.d/1584.fixed.rst +++ b/doc/changelog.d/1584.fixed.rst @@ -1,3 +1,3 @@ -Raise :class:`~can.io.blf.BLFParseError` for objects inside BLF log containers +Raise ``BLFParseError`` for objects inside BLF log containers that are smaller than their base header instead of repeatedly reading a zero-sized object.