Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
31 changes: 20 additions & 11 deletions Lib/plistlib.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -73,6 +73,9 @@
PlistFormat=enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
globals().update(PlistFormat.__members__)

# Data larger than this will be read in chunks, to prevent extreme
# overallocation.
_MIN_READ_BUF_SIZE=1<<20

classUID:
def__init__(self, data):
Expand DownExpand Up@@ -499,12 +502,24 @@ def _get_size(self, tokenL):

returntokenL

def_read(self, size):
cursize=min(size, _MIN_READ_BUF_SIZE)
data=self._fp.read(cursize)
whileTrue:
iflen(data) !=cursize:
raiseInvalidFileException
ifcursize==size:
returndata
delta=min(cursize, size-cursize)
data+=self._fp.read(delta)
cursize+=delta

def_read_ints(self, n, size):
data=self._fp.read(size*n)
data=self._read(size*n)
ifsizein_BINARY_FORMAT:
returnstruct.unpack(f'>{n}{_BINARY_FORMAT[size]}', data)
else:
ifnotsizeorlen(data) !=size*n:
ifnotsize:
raiseInvalidFileException()
returntuple(int.from_bytes(data[i: i+size], 'big')
foriinrange(0, size*n, size))
Expand DownExpand Up@@ -561,22 +576,16 @@ def _read_object(self, ref):

eliftokenH==0x40: # data
s=self._get_size(tokenL)
result=self._fp.read(s)
iflen(result) !=s:
raiseInvalidFileException()
result=self._read(s)

eliftokenH==0x50: # ascii string
s=self._get_size(tokenL)
data=self._fp.read(s)
iflen(data) !=s:
raiseInvalidFileException()
data=self._read(s)
result=data.decode('ascii')

eliftokenH==0x60: # unicode string
s=self._get_size(tokenL) *2
data=self._fp.read(s)
iflen(data) !=s:
raiseInvalidFileException()
data=self._read(s)
result=data.decode('utf-16be')

eliftokenH==0x80: # UID
Expand Down
37 changes: 34 additions & 3 deletions Lib/test/test_plistlib.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -841,8 +841,7 @@ def test_xml_plist_with_entity_decl(self):

classTestBinaryPlistlib(unittest.TestCase):

@staticmethod
defdecode(*objects, offset_size=1, ref_size=1):
defbuild(self, *objects, offset_size=1, ref_size=1):
data= [b'bplist00']
offset=8
offsets= []
Expand All@@ -854,7 +853,11 @@ def decode(*objects, offset_size=1, ref_size=1):
len(objects), 0, offset)
data.extend(offsets)
data.append(tail)
returnplistlib.loads(b''.join(data), fmt=plistlib.FMT_BINARY)
returnb''.join(data)

defdecode(self, *objects, offset_size=1, ref_size=1):
data=self.build(*objects, offset_size=offset_size, ref_size=ref_size)
returnplistlib.loads(data, fmt=plistlib.FMT_BINARY)

deftest_nonstandard_refs_size(self):
# Issue #21538: Refs and offsets are 24-bit integers
Expand DownExpand Up@@ -963,6 +966,34 @@ def test_invalid_binary(self):
withself.assertRaises(plistlib.InvalidFileException):
plistlib.loads(b'bplist00'+data, fmt=plistlib.FMT_BINARY)

deftest_truncated_large_data(self):
self.addCleanup(os_helper.unlink, os_helper.TESTFN)
defcheck(data):
withopen(os_helper.TESTFN, 'wb') asf:
f.write(data)
# buffered file
withopen(os_helper.TESTFN, 'rb') asf:
withself.assertRaises(plistlib.InvalidFileException):
plistlib.load(f, fmt=plistlib.FMT_BINARY)
# unbuffered file
withopen(os_helper.TESTFN, 'rb', buffering=0) asf:
withself.assertRaises(plistlib.InvalidFileException):
plistlib.load(f, fmt=plistlib.FMT_BINARY)
forwinrange(20, 64):
s=1<<w
# data
check(self.build(b'\x4f\x13'+s.to_bytes(8, 'big')))
# ascii string
check(self.build(b'\x5f\x13'+s.to_bytes(8, 'big')))
# unicode string
check(self.build(b'\x6f\x13'+s.to_bytes(8, 'big')))
# array
check(self.build(b'\xaf\x13'+s.to_bytes(8, 'big')))
# dict
check(self.build(b'\xdf\x13'+s.to_bytes(8, 'big')))
# number of objects
check(b'bplist00'+struct.pack('>6xBBQQQ', 1, 1, s, 0, 8))


classTestKeyedArchive(unittest.TestCase):
deftest_keyed_archive_data(self):
Expand Down
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
Fix a potential memory denial of service in the :mod:`plistlib` module.
When reading a Plist file received from untrusted source, it could cause
an arbitrary amount of memory to be allocated.
This could have led to symptoms including a :exc:`MemoryError`, swapping, out
of memory (OOM) killed processes or containers, or even system crashes.
Loading