Crash after unpickling hard switched tasklet #61

Description
Originally reported by: Anselm Kruis (Bitbucket: akruis, GitHub: akruis)
Hi,
a few weeks ago I observed crashes during the development of pyheapdump. Now I had some time to track them down and to write a small example.
The attached script first pickles a single frame and unpickles the frame. Then it accesses frame.f_locals. Python crashes in function map_to_dict() from frameobject.c. This function is invoked from PyFrame_FastToLocals().
Obviously we have two bugs:
-
Upon unpickling python fails to set up frame->f_localsplus correctly (== as expected by PyFrame_FastToLocals) in function frame_setstate from prickelpit.c. The localsplus array must start with frame->f_code->co_nlocals local variables, followed by len(frame->f_code->co_cellvars) + len(frame->f_code->co_freevars) cell objects.
-
Upon pickling python creates an inconsistent object state for the frame in function frameobject_reduce from prickelpit.c. If the frame has no stacktop, python omits the localsplus array from the pickle. This would be correct, if the only valid use case for pickling frames was pickling, unpickling and resuming of tasklets. But is is also possible to pickle/unpickle a tasklet with the sole intent to inspect it using a debugger.
Plan
I'll add test cases for the two bugs and prepare pull requests with fixes.
Here is the code:
#!python
import pickle
import stackless
def pickle_current_frame():
result = []
def func(current):
result.append(pickle.dumps(current.frame, -1))
stackless.tasklet().bind(func, (stackless.current,)).run()
return result[0]
soft = stackless.enable_softswitch(False) # no crash, if soft switching
try:
p = pickle_current_frame()
finally:
stackless.enable_softswitch(soft)
frame = pickle.loads(p)
frame.f_locals # this line crashes Stackless
print("No Crash, OK")