Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix reading for long MakerNote and UserComment #165

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 43 additions & 41 deletions exifread/classes.py
Expand Up @@ -97,6 +97,8 @@ def s2n(self, offset, length: int, signed=False) -> int:
raise ValueError('unexpected unpacking length: %d' % length) from err
self.file_handle.seek(self.offset + offset)
buf = self.file_handle.read(length)
if len(buf) != length:
raise ValueError("cannot read enough bytes, something elsewhere is wrong")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prevents infinite loop in some tests. But it will not be fixed until the parsing of Canon's MakerNote being fixed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires further investigation.


if buf:
# https://github.com/ianare/exif-py/pull/158
Expand Down Expand Up @@ -145,47 +147,36 @@ def list_ifd(self) -> list:
def _process_field(self, tag_name, count, field_type, type_length, offset):
values = []
signed = (field_type in [6, 8, 9, 10])
# XXX investigate
# some entries get too big to handle could be malformed
# file or problem with self.s2n
if count < 1000:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot just ignore large entries other than MakerNote.

for _ in range(count):
if field_type in (5, 10):
# a ratio
value = Ratio(
self.s2n(offset, 4, signed),
self.s2n(offset + 4, 4, signed)
)
elif field_type in (11, 12):
# a float or double
unpack_format = ''
if self.endian == 'I':
unpack_format += '<'
else:
unpack_format += '>'
if field_type == 11:
unpack_format += 'f'
else:
unpack_format += 'd'
self.file_handle.seek(self.offset + offset)
byte_str = self.file_handle.read(type_length)
try:
value = struct.unpack(unpack_format, byte_str)
except struct.error:
logger.warning('Possibly corrupted field %s', tag_name)
# -1 means corrupted
value = -1
for _ in range(count):
if field_type in (5, 10):
# a ratio
value = Ratio(
self.s2n(offset, 4, signed),
self.s2n(offset + 4, 4, signed)
)
elif field_type in (11, 12):
# a float or double
unpack_format = ''
if self.endian == 'I':
unpack_format += '<'
else:
value = self.s2n(offset, type_length, signed)
values.append(value)
offset = offset + type_length
# The test above causes problems with tags that are
# supposed to have long values! Fix up one important case.
elif tag_name in ('MakerNote', makernote.canon.CAMERA_INFO_TAG_NAME):
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to function _process_field7.

for _ in range(count):
unpack_format += '>'
if field_type == 11:
unpack_format += 'f'
else:
unpack_format += 'd'
self.file_handle.seek(self.offset + offset)
byte_str = self.file_handle.read(type_length)
try:
value = struct.unpack(unpack_format, byte_str)
except struct.error:
logger.warning('Possibly corrupted field %s', tag_name)
# -1 means corrupted
value = -1
else:
value = self.s2n(offset, type_length, signed)
values.append(value)
offset = offset + type_length
values.append(value)
offset = offset + type_length
return values

def _process_field2(self, ifd_name, tag_name, count, offset):
Expand Down Expand Up @@ -214,6 +205,15 @@ def _process_field2(self, ifd_name, tag_name, count, offset):
values = ''
return values

def _process_field7(self, count, offset):
# undefined types e.g. MakerNote/UserComment
values = []
for _ in range(count):
value = self.s2n(offset, 1, signed=False)
values.append(value)
offset += 1
return values

def _process_tag(self, ifd, ifd_name: str, tag_entry, entry, tag: int, tag_name, relative, stop_tag) -> None:
field_type = self.s2n(entry + 2, 2)

Expand Down Expand Up @@ -248,9 +248,11 @@ def _process_tag(self, ifd, ifd_name: str, tag_entry, entry, tag: int, tag_name,

field_offset = offset
values = None
if field_type == 2:
if field_type == 2: # ascii strings
values = self._process_field2(ifd_name, tag_name, count, offset)
else:
elif field_type == 7: # undefined
values = self._process_field7(count, offset)
else: # numbers
values = self._process_field(tag_name, count, field_type, type_length, offset)
# now 'values' is either a string or an array
# TODO: use only one type
Expand Down