Feature or enhancement
Proposal:
I want to pickle and unpickle data that may validly contain types.FunctionType instances, but I want to prevent them from being used in REDUCE opcodes, to protect me from RCEs like REDUCE os.system('firefox https://www.youtube.com/watch?v=dQw4w9WgXcQ').
The traditional conservative approach is to use an allowlist, e.g.:
class SafeUnpickler(pickle.Unpickler):
def find_class(self, module, name):
obj = super().find_class(module, name)
if not known_to_be_safe(obj):
raise pickle.UnpicklingError(f"unsupported symbol '{module}.{name}'")
return obj
However, find_class is applied when a GLOBAL/STACK_GLOBAL opcode is executed, before the interpreter knows whether the symbol will be used as a value or passed to REDUCE. I want to permit pickling of any function, including os.system; I just don't want to allow passing it to REDUCE.
So, what I really want is some sort of hook called at REDUCE time. One concrete proposal:
Ts = TypeVarTuple('Ts')
R = TypeVar('R')
def reconstruct(self, constructor: Callable[[*Ts], R], args: tuple[*Ts]) -> R:
# default impl; overrides may raise UnpicklingError
return constructor(*args)
The safe idiom would be:
known_safe_classes = (SomeClass, OtherClass)
def reconstruct(self, constructor: Callable[[*Ts], R], args: tuple[*Ts]) -> R:
if not isinstance(constructor, type):
# prevent e.g. os.system RCE
raise UnpicklingError(...)
if not issubclass(constructor, known_safe_classes):
# prevent e.g. subprocess.Popen RCE
raise UnpicklingError(...)
return constructor(*args)
Dismissed alternatives:
- I can solve my problem by prohibiting functions in
find_class, and instead providing a custom serialization for FunctionType through persistent_id/persistent_load, but this is unfortunate since I need to re-implement serialization from scratch of a type that is already supported natively.
- I can solve my problem by subclassing the undocumented pure-python
pickle._Unpickler instead of pickle.Unpickler, and overriding the undocumented load_reduce (which cPickle's Unpickler does not honor). This is undocumented and slow.
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Feature or enhancement
Proposal:
I want to pickle and unpickle data that may validly contain
types.FunctionTypeinstances, but I want to prevent them from being used in REDUCE opcodes, to protect me from RCEs likeREDUCE os.system('firefox https://www.youtube.com/watch?v=dQw4w9WgXcQ').The traditional conservative approach is to use an allowlist, e.g.:
However, find_class is applied when a
GLOBAL/STACK_GLOBALopcode is executed, before the interpreter knows whether the symbol will be used as a value or passed toREDUCE. I want to permit pickling of any function, including os.system; I just don't want to allow passing it to REDUCE.So, what I really want is some sort of hook called at
REDUCEtime. One concrete proposal:The safe idiom would be:
Dismissed alternatives:
find_class, and instead providing a custom serialization forFunctionTypethroughpersistent_id/persistent_load, but this is unfortunate since I need to re-implement serialization from scratch of a type that is already supported natively.pickle._Unpicklerinstead ofpickle.Unpickler, and overriding the undocumentedload_reduce(which cPickle's Unpickler does not honor). This is undocumented and slow.Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response