Skip to content

Current default implementation of __getstate__ and __setstate__ could be made safer #1004

@djipko

Description

@djipko

This is a known "sharp edge" of pickle, but attrs could make this a bit safer by slightly modifying the default implementation of __getstate__ and __setstate__. The problem is that due to returning a tuple in the default implementation of __getstate__, removing a member, and then unpickling from a previous version can be very unsafe. This is not a hypothetical situation - it commonly happens when an object is pickled, and then stored in a data-store of some sort, and unpickled some time later by a changed version of the code.

Here's a simple reproducer that demonstrates it (python 3.10, attrs 22.1.0):

import pickle

import attr

@attr.s(slots=True, hash=False, auto_attribs=True)
class Test:
    count: int
    enable_minor_feature: bool
    should_launch_missiles: bool

t = Test(count=1, should_launch_missiles=False, enable_minor_feature=True)
print(t)  # Test(count=1, enable_minor_feature=True, should_launch_missiles=False)
tp = pickle.dumps(t)

@attr.s(slots=True, hash=False, auto_attribs=True)
class Test:
    count: int
    should_launch_missiles: bool

tl = pickle.loads(tp)
print(tl)  # Test(count=1, should_launch_missiles=True)  <== an attribute is assigned a dangerously wrong value

While there's certainly an argument to be made that pickle should not be used for such things (and I'd agree) - the default implementation could, I believe, be made safer. Raise if things don't match, or even ignore unknown attributes in setstate, by potentially returning a dictionary instead of a tuple here would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions