-
-
Notifications
You must be signed in to change notification settings - Fork 34k
Description
Bug report
Bug description:
When load_reduce() is called in Python pickle, the args variable is pulled off the stack and passed into the functions using the * operator. This syntax is type-permissive, meaning the type can be any iterable that will return the needed arguments.
Lines 1723 to 1727 in 29acc08
| def load_reduce(self): | |
| stack = self.stack | |
| args = stack.pop() | |
| func = stack[-1] | |
| stack[-1] = func(*args) |
However, in C _pickle, argtup is pulled off the stack and passed into PyObject_CallObject(), which checks if the args parameter is specifically a PyTuple().
Line 6964 in 29acc08
| obj = PyObject_CallObject(callable, argtup); |
Lines 460 to 473 in 29acc08
| PyObject_CallObject(PyObject *callable, PyObject *args) | |
| { | |
| PyThreadState *tstate = _PyThreadState_GET(); | |
| assert(!_PyErr_Occurred(tstate)); | |
| if (args == NULL) { | |
| return _PyObject_CallNoArgsTstate(tstate, callable); | |
| } | |
| if (!PyTuple_Check(args)) { | |
| _PyErr_SetString(tstate, PyExc_TypeError, | |
| "argument list must be a tuple"); | |
| return NULL; | |
| } | |
| return _PyObject_Call(tstate, callable, args, NULL); | |
| } |
This means that any non-tuple iterator being used as the arguments passed into the function will cause C _pickle to throw an error, but Python pickle deserialization to unfold just fine.
payload: b'cbuiltins\nprint\n}R.'
pickle:
None
_pickle.c: FAILURE argument list must be a tuple
pickletools:
0: c GLOBAL 'builtins print'
16: } EMPTY_DICT
17: R REDUCE
18: . STOP
highest protocol among opcodes = 1
I think the easiest way to remedy the discrepancy is to explicitly type check the args parameter in pickle.py's load_reduce() function.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status