Repository navigation
Conversation
…coder The NSKeyedArchiver object decoder re-decoded a shared object every time it was referenced, so a plist whose references fan out onto the same shared subtree expanded into exponential decode operations. Decoded objects are now reused by their plist UID, with the existing cycle check retained and path-dependent results left uncached, keeping decoded values identical.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5326 +/- ##
==========================================
+ Coverage 85.54% 85.57% +0.02%
==========================================
Files 458 458
Lines 41563 41633 +70
==========================================
+ Hits 35555 35626 +71
+ Misses 6008 6007 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
joachimmetz
reviewed
Oct 10, 2026
| def __init__(self): | ||
| """Initializes a NSKeyedArchiver decoder.""" | ||
| super().__init__() | ||
| self._cycle_skip_count = 0 |
Member
There was a problem hiding this comment.
Change "_cycle_skip_count" into a name that closer matches it purpose
joachimmetz
reviewed
Oct 10, 2026
|
|
||
| test_decoder = interface.NSKeyedArchiverDecoder() | ||
|
|
||
| with mock.patch.object( |
Member
There was a problem hiding this comment.
please solve this without mock
…rom its test Renamed _cycle_skip_count to _number_of_cyclic_references so the name reflects that it counts references skipped because they point back to an object already on the current decode path. Reworked testDecodeWithSharedReferences to verify shared-reference decoding without mock: it asserts each reference resolves to the same decoded object and that decoding terminates and yields the correct values. The cyclic-structure test remains mock-free.
Contributor
Author
|
Addressed both review points in 5ac3375:
The decode-once property is stated in the PR description. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BLUF
NSKeyedArchiverDecoderre-decoded a shared object every time it was referenced; a plist whose references fan out onto the same shared subtree expanded into an exponential number of decode operations even for a tiny object table.NSKeyedArchiver.plist, and cyclic-structure output all confirm identical results with a linear decode count.Details
NSKeyedArchiverDecoder.Decodewalks object references (CF$UIDvalues) intothe
$objectsarray. Previously each reference was decoded on its own via_DecodeObject(objects_array[uid], ...)with only a path-based cycle check andno reuse of already-decoded objects. A reference fan-out onto one shared subtree
therefore re-decoded the same objects repeatedly, which grows exponentially:
The call count doubles with every added level while the object table stays tiny.
The fix adds a per-plist cache keyed by plist UID. A new
_DecodeReferencedObjecthelper returns the cached value on a repeat referenceand otherwise decodes and stores it. To keep behaviour identical for objects
that reference each other, a frame is only cached when no cycle reference was
skipped anywhere in its subtree (tracked with a running
_cycle_skip_count);any object on a cycle is fully traversed on its first visit, hits a
back-reference, and is therefore not cached, so its path-dependent value is
preserved. The cache is reset at the start of each
Decodecall and clearedafterwards, because a decoder instance is reused across plists
(
macos_background_items,sqlite_plugins/ios_instagram).References to the same UID now yield the same Python object rather than a fresh
copy. Decoded plist values are consumed read-only in
plaso/parsers/plist_plugins/and
ios_instagram(grepped: nopop/del/in-place mutation of decoded values),so sharing is safe; equality of results is unchanged.
Verified:
_DecodeObjectcalls (~4.6 s)on main, doubling per level; after the fix → 21 calls.
tests/parsers/plist_plugins/interface,macos_background_items,sqlite_plugins/ios_instagram,parsers/plist.test_data/plist/NSKeyedArchiver.plistdecodes to an equal value before andafter.
{'a': {'b': {}}, 'b': {'a': {}}}before and after.black --check,pylint --rcfile=.pylintrc,typos.Fixes #5325