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

bpo-36907: fix refcount bug in _PyStack_UnpackDict() #13381

Merged
merged 2 commits into from May 22, 2019

Conversation

jdemeyer
Copy link
Contributor

@jdemeyer jdemeyer commented May 17, 2019

@jdemeyer
Copy link
Author

@jdemeyer jdemeyer commented May 17, 2019

I decided to increase the refcount in _PyStack_UnpackDict after all because that seemed the most natural place (and because I didn't want to contradict the "borrowed references" comments). If you disagree, it would also be possible to leave _PyStack_UnpackDict alone and increase the refcounts in the code calling _PyStack_UnpackDict.

This should probably be backported to 3.7 too.

for (i = 0; i < nargs; i++) {
Py_INCREF(args[i]);
stack[i] = args[i];
}
Copy link
Member

@encukou encukou May 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to not keep memcpy for the bulk copy, and only add an INCREF loop?

Copy link
Contributor Author

@jdemeyer jdemeyer May 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to not keep memcpy for the bulk copy, and only add an INCREF loop?

Not really, it's mostly a matter of style. I like doing the INCREF at the same time as the assignment. I also find the for loop variant simpler to understand.

Lib/test/test_call.py Show resolved Hide resolved
self.kwargs = kwargs
def __index__(self):
self.kwargs.clear()
L = [2**i for i in range(10000)]
Copy link
Member

@encukou encukou May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to trigger garbage collection, right? Why not make it explicit?

Suggested change
L = [2**i for i in range(10000)]
gc.collect()

(with import gc at the top)

Copy link
Contributor Author

@jdemeyer jdemeyer May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is supposed to trigger garbage collection, right? Why not make it explicit?

Not really, it is meant to clobber memory. The bug is an use-after-free but crashing is only guaranteed if the used-after-freed memory is corrupted. But I'll add a comment explaining that.

Copy link
Member

@encukou encukou May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we just leave that to the debug allocator, or valgrind runs.

Copy link
Contributor Author

@jdemeyer jdemeyer May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we just leave that to the debug allocator, or valgrind runs.

Fine for me, but I have to mention that a similar thing was accepted by you(!) in #9084.

Copy link
Member

@encukou encukou May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope the things I learned since then are useful.

@vstinner, what's your view on this? Is it useful to clobber memory with L = [2**i for i in range(10000)]?

Copy link
Member

@vstinner vstinner May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstinner, what's your view on this? Is it useful to clobber memory with L = [2**i for i in range(10000)]?

I'm sorry but I don't have the bandwidth right now to follow the discussion on PEP 590 and it's implementation :-( Retry to ping me in 1 or 2 weeks if you want ;-)

Copy link
Member

@encukou encukou May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vstinner Ah, sorry. This isn't really about PEP 590, though. It's a refcounting bug, an object is deallocated while it's still in use.

In normal builds, the bug will manifest only if you clobber the memory after it is deallocated. The debug allocator (or valgrind) should catch it even if the L = [2**i for i in range(10000)] is not done.

Is it OK to rely on the debug allocator (or valgrind) to prevent regressions?

(If you don't have bandwidth for that either, let me know)

Copy link
Contributor Author

@jdemeyer jdemeyer May 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the mean time, I removed that line.

@jdemeyer jdemeyer force-pushed the _PyStack_UnpackDict_refcount branch from 7f45962 to bdea0de Compare May 21, 2019
@jdemeyer jdemeyer force-pushed the _PyStack_UnpackDict_refcount branch from bdea0de to c047904 Compare May 21, 2019
@jdemeyer
Copy link
Author

@jdemeyer jdemeyer commented May 22, 2019

Can this be merged please? That would be good for the PEP 590 implementation, which uses this function.

Copy link
Member

@encukou encukou left a comment

I took the liberty to add a gc.collect() call.

@encukou encukou merged commit 77aa396 into python:master May 22, 2019
5 checks passed
@encukou
Copy link

@encukou encukou commented May 22, 2019

#13493 is a backport to 3.7.

@jdemeyer jdemeyer deleted the _PyStack_UnpackDict_refcount branch May 22, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants