Skip to content

Python pickle load_reduce function doesn't check if args is a tuple #144412

@Legoclones

Description

@Legoclones

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.

cpython/Lib/pickle.py

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().

obj = PyObject_CallObject(callable, argtup);

cpython/Objects/call.c

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

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions