python / python/cpython

`pickle.Unpickler`: add hook to intercept `REDUCE` calls

オープン
#153,347 コメント 3 件 リアクション 0 件 担当者 1 名 GitHub で見る

@serhiy-storchaka がすでに取り組んでいます。

2026年7月9日 から。

extension-modules pending type-feature
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

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

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。