Skip to content

Commit

Permalink
bpo-42103: Improve validation of Plist files. (GH-22882)
Browse files Browse the repository at this point in the history
* Prevent some possible DoS attacks via providing invalid Plist files
  with extremely large number of objects or collection sizes.
* Raise InvalidFileException for too large bytes and string size instead of returning garbage.
* Raise InvalidFileException instead of ValueError for specific invalid datetime (NaN).
* Raise InvalidFileException instead of TypeError for non-hashable dict keys.
* Add more tests for invalid Plist files.
(cherry picked from commit 34637a0)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
  • Loading branch information
miss-islington and serhiy-storchaka committed Nov 2, 2020
1 parent 723e21a commit e277cb7
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 57 deletions.
26 changes: 18 additions & 8 deletions Lib/plistlib.py
Expand Up @@ -477,7 +477,7 @@ def parse(self, fp):
return self._read_object(top_object)

except (OSError, IndexError, struct.error, OverflowError,
UnicodeDecodeError):
ValueError):
raise InvalidFileException()

def _get_size(self, tokenL):
Expand All @@ -493,7 +493,7 @@ def _get_size(self, tokenL):
def _read_ints(self, n, size):
data = self._fp.read(size * n)
if size in _BINARY_FORMAT:
return struct.unpack('>' + _BINARY_FORMAT[size] * n, data)
return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
else:
if not size or len(data) != size * n:
raise InvalidFileException()
Expand Down Expand Up @@ -553,14 +553,22 @@ def _read_object(self, ref):
elif tokenH == 0x40: # data
s = self._get_size(tokenL)
result = self._fp.read(s)
if len(result) != s:
raise InvalidFileException()

elif tokenH == 0x50: # ascii string
s = self._get_size(tokenL)
result = self._fp.read(s).decode('ascii')
data = self._fp.read(s)
if len(data) != s:
raise InvalidFileException()
result = data.decode('ascii')

elif tokenH == 0x60: # unicode string
s = self._get_size(tokenL)
result = self._fp.read(s * 2).decode('utf-16be')
s = self._get_size(tokenL) * 2
data = self._fp.read(s)
if len(data) != s:
raise InvalidFileException()
result = data.decode('utf-16be')

elif tokenH == 0x80: # UID
# used by Key-Archiver plist files
Expand All @@ -585,9 +593,11 @@ def _read_object(self, ref):
obj_refs = self._read_refs(s)
result = self._dict_type()
self._objects[ref] = result
for k, o in zip(key_refs, obj_refs):
result[self._read_object(k)] = self._read_object(o)

try:
for k, o in zip(key_refs, obj_refs):
result[self._read_object(k)] = self._read_object(o)
except TypeError:
raise InvalidFileException()
else:
raise InvalidFileException()

Expand Down

0 comments on commit e277cb7

Please sign in to comment.