Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Lib/plistlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 @@ -556,11 +556,17 @@ def _read_object(self, ref):

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 Down
247 changes: 199 additions & 48 deletions Lib/test/test_plistlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,201 @@
</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'
),
('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 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'
),
]


class TestPlistlib(unittest.TestCase):

Expand Down Expand Up @@ -619,54 +814,10 @@ def test_large_timestamp(self):
self.assertEqual(plistlib.loads(data), d)

def test_invalid_binary(self):
for data in [
# too short data
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):
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)
for name, data in INVALID_BINARY_PLISTS:
with self.subTest(name):
with self.assertRaises(plistlib.InvalidFileException):
plistlib.loads(b'bplist00' + data, fmt=plistlib.FMT_BINARY)


class TestKeyedArchive(unittest.TestCase):
Expand Down