bpo-42103: Improve validation of Plist files. (GH-22882)
* 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.
This commit is contained in:
parent
6fdfcec5b1
commit
34637a0ce2
|
@ -477,7 +477,7 @@ class _BinaryPlistParser:
|
||||||
return self._read_object(top_object)
|
return self._read_object(top_object)
|
||||||
|
|
||||||
except (OSError, IndexError, struct.error, OverflowError,
|
except (OSError, IndexError, struct.error, OverflowError,
|
||||||
UnicodeDecodeError):
|
ValueError):
|
||||||
raise InvalidFileException()
|
raise InvalidFileException()
|
||||||
|
|
||||||
def _get_size(self, tokenL):
|
def _get_size(self, tokenL):
|
||||||
|
@ -493,7 +493,7 @@ class _BinaryPlistParser:
|
||||||
def _read_ints(self, n, size):
|
def _read_ints(self, n, size):
|
||||||
data = self._fp.read(size * n)
|
data = self._fp.read(size * n)
|
||||||
if size in _BINARY_FORMAT:
|
if size in _BINARY_FORMAT:
|
||||||
return struct.unpack('>' + _BINARY_FORMAT[size] * n, data)
|
return struct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
|
||||||
else:
|
else:
|
||||||
if not size or len(data) != size * n:
|
if not size or len(data) != size * n:
|
||||||
raise InvalidFileException()
|
raise InvalidFileException()
|
||||||
|
@ -553,14 +553,22 @@ class _BinaryPlistParser:
|
||||||
elif tokenH == 0x40: # data
|
elif tokenH == 0x40: # data
|
||||||
s = self._get_size(tokenL)
|
s = self._get_size(tokenL)
|
||||||
result = self._fp.read(s)
|
result = self._fp.read(s)
|
||||||
|
if len(result) != s:
|
||||||
|
raise InvalidFileException()
|
||||||
|
|
||||||
elif tokenH == 0x50: # ascii string
|
elif tokenH == 0x50: # ascii string
|
||||||
s = self._get_size(tokenL)
|
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
|
elif tokenH == 0x60: # unicode string
|
||||||
s = self._get_size(tokenL)
|
s = self._get_size(tokenL) * 2
|
||||||
result = self._fp.read(s * 2).decode('utf-16be')
|
data = self._fp.read(s)
|
||||||
|
if len(data) != s:
|
||||||
|
raise InvalidFileException()
|
||||||
|
result = data.decode('utf-16be')
|
||||||
|
|
||||||
elif tokenH == 0x80: # UID
|
elif tokenH == 0x80: # UID
|
||||||
# used by Key-Archiver plist files
|
# used by Key-Archiver plist files
|
||||||
|
@ -585,9 +593,11 @@ class _BinaryPlistParser:
|
||||||
obj_refs = self._read_refs(s)
|
obj_refs = self._read_refs(s)
|
||||||
result = self._dict_type()
|
result = self._dict_type()
|
||||||
self._objects[ref] = result
|
self._objects[ref] = result
|
||||||
|
try:
|
||||||
for k, o in zip(key_refs, obj_refs):
|
for k, o in zip(key_refs, obj_refs):
|
||||||
result[self._read_object(k)] = self._read_object(o)
|
result[self._read_object(k)] = self._read_object(o)
|
||||||
|
except TypeError:
|
||||||
|
raise InvalidFileException()
|
||||||
else:
|
else:
|
||||||
raise InvalidFileException()
|
raise InvalidFileException()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import copy
|
import copy
|
||||||
import operator
|
import operator
|
||||||
import pickle
|
import pickle
|
||||||
|
import struct
|
||||||
import unittest
|
import unittest
|
||||||
import plistlib
|
import plistlib
|
||||||
import os
|
import os
|
||||||
|
@ -119,6 +120,285 @@ XML_PLIST_WITH_ENTITY=b'''\
|
||||||
</plist>
|
</plist>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
INVALID_BINARY_PLISTS = [
|
||||||
|
('too short data',
|
||||||
|
b''
|
||||||
|
),
|
||||||
|
('too large offset_table_offset and offset_size = 1',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x2a'
|
||||||
|
),
|
||||||
|
('too large offset_table_offset and nonstandard offset_size',
|
||||||
|
b'\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x03\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x2c'
|
||||||
|
),
|
||||||
|
('integer overflow in offset_table_offset',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
),
|
||||||
|
('too large top_object',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('integer overflow in top_object',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('too large num_objects and offset_size = 1',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('too large num_objects and nonstandard offset_size',
|
||||||
|
b'\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x03\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('extremally large num_objects (32 bit)',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x7f\xff\xff\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('extremally large num_objects (64 bit)',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\xff\xff\xff\xff\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('integer overflow in num_objects',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('offset_size = 0',
|
||||||
|
b'\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('ref_size = 0',
|
||||||
|
b'\xa1\x01\x00\x08\x0a'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0b'
|
||||||
|
),
|
||||||
|
('too large offset',
|
||||||
|
b'\x00\x2a'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('integer overflow in offset',
|
||||||
|
b'\x00\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x08\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x09'
|
||||||
|
),
|
||||||
|
('too large array size',
|
||||||
|
b'\xaf\x00\x01\xff\x00\x08\x0c'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0d'
|
||||||
|
),
|
||||||
|
('extremally large array size (32-bit)',
|
||||||
|
b'\xaf\x02\x7f\xff\xff\xff\x01\x00\x08\x0f'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x10'
|
||||||
|
),
|
||||||
|
('extremally large array size (64-bit)',
|
||||||
|
b'\xaf\x03\x00\x00\x00\xff\xff\xff\xff\xff\x01\x00\x08\x13'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x14'
|
||||||
|
),
|
||||||
|
('integer overflow in array size',
|
||||||
|
b'\xaf\x03\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00\x08\x13'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x14'
|
||||||
|
),
|
||||||
|
('too large reference index',
|
||||||
|
b'\xa1\x02\x00\x08\x0a'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0b'
|
||||||
|
),
|
||||||
|
('integer overflow in reference index',
|
||||||
|
b'\xa1\xff\xff\xff\xff\xff\xff\xff\xff\x00\x08\x11'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x12'
|
||||||
|
),
|
||||||
|
('too large bytes size',
|
||||||
|
b'\x4f\x00\x23\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0c'
|
||||||
|
),
|
||||||
|
('extremally large bytes size (32-bit)',
|
||||||
|
b'\x4f\x02\x7f\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0f'
|
||||||
|
),
|
||||||
|
('extremally large bytes size (64-bit)',
|
||||||
|
b'\x4f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x13'
|
||||||
|
),
|
||||||
|
('integer overflow in bytes size',
|
||||||
|
b'\x4f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x13'
|
||||||
|
),
|
||||||
|
('too large ASCII size',
|
||||||
|
b'\x5f\x00\x23\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0c'
|
||||||
|
),
|
||||||
|
('extremally large ASCII size (32-bit)',
|
||||||
|
b'\x5f\x02\x7f\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0f'
|
||||||
|
),
|
||||||
|
('extremally large ASCII size (64-bit)',
|
||||||
|
b'\x5f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x13'
|
||||||
|
),
|
||||||
|
('integer overflow in ASCII size',
|
||||||
|
b'\x5f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x41\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x13'
|
||||||
|
),
|
||||||
|
('invalid ASCII',
|
||||||
|
b'\x51\xff\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0a'
|
||||||
|
),
|
||||||
|
('too large UTF-16 size',
|
||||||
|
b'\x6f\x00\x13\x20\xac\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0e'
|
||||||
|
),
|
||||||
|
('extremally large UTF-16 size (32-bit)',
|
||||||
|
b'\x6f\x02\x4f\xff\xff\xff\x20\xac\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x11'
|
||||||
|
),
|
||||||
|
('extremally large UTF-16 size (64-bit)',
|
||||||
|
b'\x6f\x03\x00\x00\x00\xff\xff\xff\xff\xff\x20\xac\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x15'
|
||||||
|
),
|
||||||
|
('integer overflow in UTF-16 size',
|
||||||
|
b'\x6f\x03\xff\xff\xff\xff\xff\xff\xff\xff\x20\xac\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x15'
|
||||||
|
),
|
||||||
|
('invalid UTF-16',
|
||||||
|
b'\x61\xd8\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0b'
|
||||||
|
),
|
||||||
|
('non-hashable key',
|
||||||
|
b'\xd1\x01\x01\xa0\x08\x0b'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x0c'
|
||||||
|
),
|
||||||
|
('too large datetime (datetime overflow)',
|
||||||
|
b'\x33\x42\x50\x00\x00\x00\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x11'
|
||||||
|
),
|
||||||
|
('too large datetime (timedelta overflow)',
|
||||||
|
b'\x33\x42\xe0\x00\x00\x00\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x11'
|
||||||
|
),
|
||||||
|
('invalid datetime (Infinity)',
|
||||||
|
b'\x33\x7f\xf0\x00\x00\x00\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x11'
|
||||||
|
),
|
||||||
|
('invalid datetime (NaN)',
|
||||||
|
b'\x33\x7f\xf8\x00\x00\x00\x00\x00\x00\x08'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||||
|
b'\x00\x00\x00\x00\x00\x00\x00\x11'
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestPlistlib(unittest.TestCase):
|
class TestPlistlib(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -558,6 +838,21 @@ class TestPlistlib(unittest.TestCase):
|
||||||
|
|
||||||
class TestBinaryPlistlib(unittest.TestCase):
|
class TestBinaryPlistlib(unittest.TestCase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode(*objects, offset_size=1, ref_size=1):
|
||||||
|
data = [b'bplist00']
|
||||||
|
offset = 8
|
||||||
|
offsets = []
|
||||||
|
for x in objects:
|
||||||
|
offsets.append(offset.to_bytes(offset_size, 'big'))
|
||||||
|
data.append(x)
|
||||||
|
offset += len(x)
|
||||||
|
tail = struct.pack('>6xBBQQQ', offset_size, ref_size,
|
||||||
|
len(objects), 0, offset)
|
||||||
|
data.extend(offsets)
|
||||||
|
data.append(tail)
|
||||||
|
return plistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)
|
||||||
|
|
||||||
def test_nonstandard_refs_size(self):
|
def test_nonstandard_refs_size(self):
|
||||||
# Issue #21538: Refs and offsets are 24-bit integers
|
# Issue #21538: Refs and offsets are 24-bit integers
|
||||||
data = (b'bplist00'
|
data = (b'bplist00'
|
||||||
|
@ -572,7 +867,7 @@ class TestBinaryPlistlib(unittest.TestCase):
|
||||||
|
|
||||||
def test_dump_duplicates(self):
|
def test_dump_duplicates(self):
|
||||||
# Test effectiveness of saving duplicated objects
|
# Test effectiveness of saving duplicated objects
|
||||||
for x in (None, False, True, 12345, 123.45, 'abcde', b'abcde',
|
for x in (None, False, True, 12345, 123.45, 'abcde', 'абвгд', b'abcde',
|
||||||
datetime.datetime(2004, 10, 26, 10, 33, 33),
|
datetime.datetime(2004, 10, 26, 10, 33, 33),
|
||||||
bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
|
bytearray(b'abcde'), [12, 345], (12, 345), {'12': 345}):
|
||||||
with self.subTest(x=x):
|
with self.subTest(x=x):
|
||||||
|
@ -609,6 +904,20 @@ class TestBinaryPlistlib(unittest.TestCase):
|
||||||
b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
|
b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
|
||||||
self.assertIs(b['x'], b)
|
self.assertIs(b['x'], b)
|
||||||
|
|
||||||
|
def test_deep_nesting(self):
|
||||||
|
for N in [300, 100000]:
|
||||||
|
chunks = [b'\xa1' + (i + 1).to_bytes(4, 'big') for i in range(N)]
|
||||||
|
try:
|
||||||
|
result = self.decode(*chunks, b'\x54seed', offset_size=4, ref_size=4)
|
||||||
|
except RecursionError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
for i in range(N):
|
||||||
|
self.assertIsInstance(result, list)
|
||||||
|
self.assertEqual(len(result), 1)
|
||||||
|
result = result[0]
|
||||||
|
self.assertEqual(result, 'seed')
|
||||||
|
|
||||||
def test_large_timestamp(self):
|
def test_large_timestamp(self):
|
||||||
# Issue #26709: 32-bit timestamp out of range
|
# Issue #26709: 32-bit timestamp out of range
|
||||||
for ts in -2**31-1, 2**31:
|
for ts in -2**31-1, 2**31:
|
||||||
|
@ -618,53 +927,35 @@ class TestBinaryPlistlib(unittest.TestCase):
|
||||||
data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
|
data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
|
||||||
self.assertEqual(plistlib.loads(data), d)
|
self.assertEqual(plistlib.loads(data), d)
|
||||||
|
|
||||||
|
def test_load_singletons(self):
|
||||||
|
self.assertIs(self.decode(b'\x00'), None)
|
||||||
|
self.assertIs(self.decode(b'\x08'), False)
|
||||||
|
self.assertIs(self.decode(b'\x09'), True)
|
||||||
|
self.assertEqual(self.decode(b'\x0f'), b'')
|
||||||
|
|
||||||
|
def test_load_int(self):
|
||||||
|
self.assertEqual(self.decode(b'\x10\x00'), 0)
|
||||||
|
self.assertEqual(self.decode(b'\x10\xfe'), 0xfe)
|
||||||
|
self.assertEqual(self.decode(b'\x11\xfe\xdc'), 0xfedc)
|
||||||
|
self.assertEqual(self.decode(b'\x12\xfe\xdc\xba\x98'), 0xfedcba98)
|
||||||
|
self.assertEqual(self.decode(b'\x13\x01\x23\x45\x67\x89\xab\xcd\xef'),
|
||||||
|
0x0123456789abcdef)
|
||||||
|
self.assertEqual(self.decode(b'\x13\xfe\xdc\xba\x98\x76\x54\x32\x10'),
|
||||||
|
-0x123456789abcdf0)
|
||||||
|
|
||||||
|
def test_unsupported(self):
|
||||||
|
unsupported = [*range(1, 8), *range(10, 15),
|
||||||
|
0x20, 0x21, *range(0x24, 0x33), *range(0x34, 0x40)]
|
||||||
|
for i in [0x70, 0x90, 0xb0, 0xc0, 0xe0, 0xf0]:
|
||||||
|
unsupported.extend(i + j for j in range(16))
|
||||||
|
for token in unsupported:
|
||||||
|
with self.subTest(f'token {token:02x}'):
|
||||||
|
with self.assertRaises(plistlib.InvalidFileException):
|
||||||
|
self.decode(bytes([token]) + b'\x00'*16)
|
||||||
|
|
||||||
def test_invalid_binary(self):
|
def test_invalid_binary(self):
|
||||||
for data in [
|
for name, data in INVALID_BINARY_PLISTS:
|
||||||
# too short data
|
with self.subTest(name):
|
||||||
b'',
|
|
||||||
# too large offset_table_offset and nonstandard offset_size
|
|
||||||
b'\x00\x08'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x03\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x2a',
|
|
||||||
# integer overflow in offset_table_offset
|
|
||||||
b'\x00\x08'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\xff\xff\xff\xff\xff\xff\xff\xff',
|
|
||||||
# offset_size = 0
|
|
||||||
b'\x00\x08'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x09',
|
|
||||||
# ref_size = 0
|
|
||||||
b'\xa1\x01\x00\x08\x0a'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x02'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x0b',
|
|
||||||
# integer overflow in offset
|
|
||||||
b'\x00\xff\xff\xff\xff\xff\xff\xff\xff'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x08\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x09',
|
|
||||||
# invalid ASCII
|
|
||||||
b'\x51\xff\x08'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x0a',
|
|
||||||
# invalid UTF-16
|
|
||||||
b'\x61\xd8\x00\x08'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x01\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x01'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
||||||
b'\x00\x00\x00\x00\x00\x00\x00\x0b',
|
|
||||||
]:
|
|
||||||
with self.assertRaises(plistlib.InvalidFileException):
|
with self.assertRaises(plistlib.InvalidFileException):
|
||||||
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
|
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
:exc:`~plistlib.InvalidFileException` and :exc:`RecursionError` are now
|
||||||
|
the only errors caused by loading malformed binary Plist file (previously
|
||||||
|
ValueError and TypeError could be raised in some specific cases).
|
|
@ -0,0 +1,2 @@
|
||||||
|
Prevented potential DoS attack via CPU and RAM exhaustion when processing
|
||||||
|
malformed Apple Property List files in binary format.
|
Loading…
Reference in New Issue