You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
22 KiB
561 lines
22 KiB
From cec66eefbee76ee95f252a52de0f5abc1b677f5b Mon Sep 17 00:00:00 2001
|
|
From: Lumir Balhar <lbalhar@redhat.com>
|
|
Date: Tue, 12 Dec 2023 13:03:42 +0100
|
|
Subject: [PATCH] [3.6] bpo-42103: Improve validation of Plist files.
|
|
(GH-22882) (GH-23118)
|
|
|
|
* 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 34637a0ce21e7261b952fbd9d006474cc29b681f)
|
|
|
|
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
|
|
---
|
|
Lib/plistlib.py | 34 +-
|
|
Lib/test/test_plistlib.py | 394 +++++++++++++++---
|
|
.../2020-10-23-19-20-14.bpo-42103.C5obK2.rst | 3 +
|
|
.../2020-10-23-19-19-30.bpo-42103.cILT66.rst | 2 +
|
|
4 files changed, 366 insertions(+), 67 deletions(-)
|
|
create mode 100644 Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst
|
|
create mode 100644 Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst
|
|
|
|
diff --git a/Lib/plistlib.py b/Lib/plistlib.py
|
|
index a918643..df1f346 100644
|
|
--- a/Lib/plistlib.py
|
|
+++ b/Lib/plistlib.py
|
|
@@ -626,7 +626,7 @@ class _BinaryPlistParser:
|
|
return self._read_object(top_object)
|
|
|
|
except (OSError, IndexError, struct.error, OverflowError,
|
|
- UnicodeDecodeError):
|
|
+ ValueError):
|
|
raise InvalidFileException()
|
|
|
|
def _get_size(self, tokenL):
|
|
@@ -642,7 +642,7 @@ class _BinaryPlistParser:
|
|
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()
|
|
@@ -701,19 +701,25 @@ class _BinaryPlistParser:
|
|
|
|
elif tokenH == 0x40: # data
|
|
s = self._get_size(tokenL)
|
|
- if self._use_builtin_types:
|
|
- result = self._fp.read(s)
|
|
- else:
|
|
- result = Data(self._fp.read(s))
|
|
+ result = self._fp.read(s)
|
|
+ if len(result) != s:
|
|
+ raise InvalidFileException()
|
|
+ if not self._use_builtin_types:
|
|
+ result = Data(result)
|
|
|
|
elif tokenH == 0x50: # ascii string
|
|
s = self._get_size(tokenL)
|
|
- result = self._fp.read(s).decode('ascii')
|
|
- result = result
|
|
+ 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')
|
|
|
|
# tokenH == 0x80 is documented as 'UID' and appears to be used for
|
|
# keyed-archiving, not in plists.
|
|
@@ -737,9 +743,11 @@ class _BinaryPlistParser:
|
|
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()
|
|
|
|
diff --git a/Lib/test/test_plistlib.py b/Lib/test/test_plistlib.py
|
|
index d47c607..f71245d 100644
|
|
--- a/Lib/test/test_plistlib.py
|
|
+++ b/Lib/test/test_plistlib.py
|
|
@@ -1,5 +1,6 @@
|
|
# Copyright (C) 2003-2013 Python Software Foundation
|
|
|
|
+import struct
|
|
import unittest
|
|
import plistlib
|
|
import os
|
|
@@ -90,6 +91,284 @@ TESTDATA={
|
|
xQHHAsQC0gAAAAAAAAIBAAAAAAAAADkAAAAAAAAAAAAAAAAAAALs'''),
|
|
}
|
|
|
|
+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):
|
|
|
|
@@ -447,6 +726,21 @@ class TestPlistlib(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):
|
|
# Issue #21538: Refs and offsets are 24-bit integers
|
|
data = (b'bplist00'
|
|
@@ -461,7 +755,7 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|
|
|
def test_dump_duplicates(self):
|
|
# 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),
|
|
plistlib.Data(b'abcde'), bytearray(b'abcde'),
|
|
[12, 345], (12, 345), {'12': 345}):
|
|
@@ -500,6 +794,20 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|
b = plistlib.loads(plistlib.dumps(a, fmt=plistlib.FMT_BINARY))
|
|
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):
|
|
# Issue #26709: 32-bit timestamp out of range
|
|
for ts in -2**31-1, 2**31:
|
|
@@ -509,55 +817,37 @@ class TestBinaryPlistlib(unittest.TestCase):
|
|
data = plistlib.dumps(d, fmt=plistlib.FMT_BINARY)
|
|
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):
|
|
- 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 TestPlistlibDeprecated(unittest.TestCase):
|
|
@@ -655,9 +945,5 @@ class MiscTestCase(unittest.TestCase):
|
|
support.check__all__(self, plistlib, blacklist=blacklist)
|
|
|
|
|
|
-def test_main():
|
|
- support.run_unittest(TestPlistlib, TestPlistlibDeprecated, MiscTestCase)
|
|
-
|
|
-
|
|
if __name__ == '__main__':
|
|
- test_main()
|
|
+ unittest.main()
|
|
diff --git a/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst b/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst
|
|
new file mode 100644
|
|
index 0000000..4eb694c
|
|
--- /dev/null
|
|
+++ b/Misc/NEWS.d/next/Library/2020-10-23-19-20-14.bpo-42103.C5obK2.rst
|
|
@@ -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).
|
|
diff --git a/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst b/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst
|
|
new file mode 100644
|
|
index 0000000..15d7b65
|
|
--- /dev/null
|
|
+++ b/Misc/NEWS.d/next/Security/2020-10-23-19-19-30.bpo-42103.cILT66.rst
|
|
@@ -0,0 +1,2 @@
|
|
+Prevented potential DoS attack via CPU and RAM exhaustion when processing
|
|
+malformed Apple Property List files in binary format.
|
|
--
|
|
2.43.0
|
|
|