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.
|
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().
|
obj = PyObject_CallObject(callable, argtup); |
|
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
Bug report
Bug description:
When
load_reduce()is called in Pythonpickle, theargsvariable 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.cpython/Lib/pickle.py
Lines 1723 to 1727 in 29acc08
However, in C
_pickle,argtupis pulled off the stack and passed intoPyObject_CallObject(), which checks if theargsparameter is specifically aPyTuple().cpython/Modules/_pickle.c
Line 6964 in 29acc08
cpython/Objects/call.c
Lines 460 to 473 in 29acc08
This means that any non-tuple iterator being used as the arguments passed into the function will cause C
_pickleto throw an error, but Pythonpickledeserialization to unfold just fine.I think the easiest way to remedy the discrepancy is to explicitly type check the
argsparameter inpickle.py'sload_reduce()function.CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs