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-31626: Fix _PyObject_DebugReallocApi() #4310

Merged
merged 1 commit into from
Nov 24, 2017
Merged

bpo-31626: Fix _PyObject_DebugReallocApi() #4310

merged 1 commit into from
Nov 24, 2017

Conversation

vstinner
Copy link
Member

@vstinner vstinner commented Nov 7, 2017

_PyObject_DebugReallocApi() now calls Py_FatalError() if realloc()
fails to shrink a memory block.

Call Py_FatalError() because _PyObject_DebugReallocApi() erased freed
bytes before realloc(), expecting that realloc() cannot fail to
shrink a memory block.

https://bugs.python.org/issue31626

@vstinner
Copy link
Member Author

vstinner commented Nov 7, 2017

As part of efforts to make Python memory allocators "correct", I propose to "fix" _PyObject_DebugReallocApi().

For the rationale, see:

@serhiy-storchaka
Copy link
Member

See @skrah arguments against this in #3844.

@vstinner
Copy link
Member Author

vstinner commented Nov 7, 2017

@skrah: In the PR #3844, you wrote "Why the fatal error? If a shrinking realloc() fails, the old memory is still valid (just too large)."

@serhiy-storchaka wrote a smart and correct implementation in PR #4210 for the master branch: save erased bytes, to be able to restore them on failure. But this implementation is complex.

Our Python 3.6 release manager, @ned-deily, is against backporting this "correct" fix to Python 3.6: "My reaction is that the risk due to complexity of the changes outweigh the benefits so I would agree with @serhiy-storchaka that we should not backport this."

In debug mode, Python 2 always erased bytes before calling realloc(). If realloc() fails on shrinking a memory block... the original memory block is left modfied (with erased bytes). My PR makes the code failing with a fatal error in this case which should never happen.

So I propose to be optimistic and use the Python 2 code in Python 3.6, but fix the code to trigger a fatal error if realloc() fails whereas it should never fail :-)

@vstinner
Copy link
Member Author

vstinner commented Nov 7, 2017

Once this PR will be merged, I will prepare a PR for Python 3.6.

@serhiy-storchaka
Copy link
Member

I'm not sure that it is worth to make the code crashing in maintained release. If the shrinking realloc() fails in real world, there is a chance that the third party code will just fail with MemoryError. Or will never read erased memory. With this change it will crash immediately. This doesn't look an enhancement or bug fix.

@@ -1562,8 +1562,14 @@ _PyObject_DebugReallocApi(char api, void *p, size_t nbytes)
* but we live with that.
*/
q = (uchar *)PyObject_Realloc(q - 2*SST, total);
if (q == NULL)
if (q == NULL) {
if (nbytes < original_nbytes) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be <=? I know it's checking the memset logic above, but I think we assume the "shrinking" to the same size always works, too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Right. I changed the code for <=

if (nbytes < original_nbytes) {
/* bpo-31626: the memset() above expects that realloc never fails
on shrinking a memory block. */
Py_FatalError("Shrinking reallocation is failed");
Copy link
Contributor

Choose a reason for hiding this comment

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

s/is//

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

_PyObject_DebugReallocApi() now calls Py_FatalError() if realloc()
fails to shrink a memory block.

Call Py_FatalError() because _PyObject_DebugReallocApi() erased freed
bytes *before* realloc(), expecting that realloc() *cannot* fail to
shrink a memory block.
@vstinner
Copy link
Member Author

vstinner commented Nov 8, 2017

@serhiy-storchaka: "If the shrinking realloc() fails in real world, there is a chance that the third party code will just fail with MemoryError. Or will never read erased memory."

If the application uses the memory block after the realloc failure, the application can crash because of a bug in Python memory allocation.

@serhiy-storchaka: "This doesn't look an enhancement or bug fix."

realloc() must leave the memory block unchanged on failure. It's not the cause currently, so it's a bug and my change is a bug fix.

@vstinner
Copy link
Member Author

vstinner commented Nov 8, 2017

@benjaminp: So, what do you think of this PR?

@vstinner
Copy link
Member Author

I will merge this PR this week if nobody complains against it.

@vstinner vstinner merged commit ed4743a into python:2.7 Nov 24, 2017
@vstinner vstinner deleted the debug_realloc branch November 24, 2017 01:13
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 this pull request may close these issues.

None yet

5 participants