Conversation
The pure Python ``_Unpickler`` raised ``KeyError`` for an unknown opcode and ``IndexError`` whenever an opcode reached past the end of the stack, while ``_pickle`` raises ``UnpicklingError`` with a descriptive message in both cases. Report the same errors, with the same messages, from the pure Python implementation, and narrow ``bad_stack_errors`` accordingly.
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.
Closes #141749. Picks up @djoume's #141754, which was closed unmerged yesterday after going stale.
The pure Python unpickler leaks
IndexErrorandKeyErrorout ofload()where the C one raisesUnpicklingError. Measured onmain, same malformed input through both implementations:pickle._Unpickler_pickle.Unpicklerb"\xff."KeyError: 255UnpicklingError: invalid load key, '\xff'.b"\x85."(TUPLE1)IndexError: list index out of rangeUnpicklingError: unpickling stack underflowb"R."(REDUCE)IndexError: pop from empty listUnpicklingError: unpickling stack underflowb"(I1\nd."(odd DICT)IndexError: list index out of rangeUnpicklingError: odd number of items for DICTb"1."(POP_MARK, no MARK)IndexError: pop from empty listUnpicklingError: could not find MARKb"(\x85."(past a MARK)IndexError: list index out of rangeUnpicklingError: unexpected MARK foundAcross sixteen such inputs: 0 of 16 agreed on
main, 16 of 16 agree on this branch, message text included.That matters beyond tidiness —
pickle.loaddocumentsUnpicklingErrorfor malformed data, so code that catches it correctly is still crashed by a hostile pickle whenever the pure Python implementation is in use.Both of @picnixz's review points from #141754
Done — the dispatch lookup and the call are separate, so a
KeyErrorraised inside a handler is no longer swallowed as an invalid opcode:@djoume answered on that thread, and the answer explains why this PR is wider than theirs:
Exactly so — which is why every handler that indexes or pops the stack is guarded here, not just the two cases. Once they all are, nothing reaches the caller as
IndexError, and the answer to the question becomes no.The two distinct C messages are preserved: a pending MARK hides the rest of the stack, so an opcode reaching past it reports
unexpected MARK foundwhile an exhausted stack reportsunpickling stack underflow.The test expectations got stricter, not looser
Worth flagging, since the diff touches existing test expectations and the policy rightly forbids loosening them. In
Lib/test/test_pickle.pythe tuples shrink:IndexErrorwas previously accepted; it no longer is. No assertion was relaxed and none was deleted.New cases in
pickletester.pypin the exact message for each invalid-opcode escape form and for each of the five distinct stack messages.Compatibility
This is a behaviour change.
UnpicklingErroris not a subclass ofIndexErrororKeyError, so code withexcept IndexErroraroundpickle.loadstops catching — but only on the pure Python path, only for malformed input, and only where the C implementation already raisedUnpicklingError. Anyone handling both implementations already had to catchUnpicklingError.mainonly; no backport.Verification
On a build of
main(3.16.0a0):Reverting
Lib/pickle.pytomainwhile keeping the tests: FAILURE.Two of the guards,
BINPERSIDandREADONLY_BUFFER, were initially reachable by no test — reverting just those two left the suite green. They now have cases, and reverting just those two fails.Misc/NEWS.d/next/Library/entry added, using @picnixz's suggested wording from that thread.