Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
djipko opened this issue Aug 12, 2022 · 4 comments · Fixed by #1009
Closed

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

djipko opened this issue Aug 12, 2022 · 4 comments · Fixed by #1009

Comments

@djipko
Copy link
Contributor

djipko commented Aug 12, 2022

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.

@hynek
Copy link
Member

hynek commented Aug 16, 2022

Have you tried replacing the tuple by the dict and see how much it breaks? Performance difference should be benign, since we save ourselves zipping.

@djipko
Copy link
Contributor Author

djipko commented Aug 16, 2022

I wanted to get some thoughts on the feasibility of making the change at all first. Let me put together a patch and see how much it breaks, and we can take it from there. Thanks :)

@hynek
Copy link
Member

hynek commented Aug 16, 2022

I don't think our implementation of pickling is a public API. So if you can make it compatible, that should be OK.

@djipko
Copy link
Contributor Author

djipko commented Aug 16, 2022

I think this should now have the right coverage, and all tests seem to pass so \o/

Let me know what you think :)

Thanks!

hynek pushed a commit that referenced this issue Aug 18, 2022
Co-authored-by: Nikola Dipanov <ndipanov@hudson-trading.com>
Zac-HD added a commit to Zac-HD/attrs that referenced this issue Sep 13, 2024
Zac-HD added a commit to Zac-HD/attrs that referenced this issue Sep 13, 2024
Zac-HD added a commit to Zac-HD/attrs that referenced this issue Sep 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants