bpo-34395: Don't free allocated memory on realloc fail in load_mark() in _pickle.c. by sir-sigurd · Pull Request #8788 · python/cpython (original) (raw)

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 andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Conversation15 Commits3 Checks0 Files changed

Conversation

This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters

[ Show hidden characters]({{ revealButtonHref }})

sir-sigurd

@sir-sigurd

serhiy-storchaka

serhiy-storchaka

@@ -6297,14 +6297,13 @@ load_mark(UnpicklerObject *self)
return -1;
}

Choose a reason for hiding this comment

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

Seems the check above can be removed. alloc > (PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) is duplicated in PyMem_RESIZE(), and failing alloc <= ((size_t)self->num_marks + 1) is not possible if sizeof(Py_ssize_t) > 2.

Choose a reason for hiding this comment

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

if ((self->num_marks + 1) >= self->marks_size) {

Also, it seems that new space is needed only when self->num_marks + 1 is strictly greater than self->marks_size.

Choose a reason for hiding this comment

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

Agree. This condition can be written as self->num_marks >= self->marks_size or self->num_marks == self->marks_size.

Choose a reason for hiding this comment

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

@serhiy-storchaka
I'd prefer to make these change as separate commits/RPs as they are not related to this bpo issue.

Choose a reason for hiding this comment

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

PR for upsizing issue #8860.

zhangyangyu

@zhangyangyu

Need backport to make code consistent or as enhancement?

@serhiy-storchaka

I don't think there is a bug that needs to be fixed. This change looks rather as a code clean up to me. If I'm wrong, and it fixes a real bug, it should be backported.

@sir-sigurd, please remove also the redundant check before PyMem_RESIZE if it is truly unneeded.

@sir-sigurd

@serhiy-storchaka

I'm considering adding this as explanation why overflow check is not needed

assert((size_t)self->num_marks <= PY_SSIZE_T_MAX / sizeof(Py_ssize_t));
Py_BUILD_ASSERT((size_t)PY_SSIZE_T_MAX / sizeof(Py_ssize_t) << 1 <= SIZE_MAX - 20);

but I'm not sure if it makes things clear.

@serhiy-storchaka

I think this is not needed. Just remove the check.

@sir-sigurd

@sir-sigurd

serhiy-storchaka

@zhangyangyu

failing alloc <= ((size_t)self->num_marks + 1) is not possible if sizeof(Py_ssize_t) > 2.

@serhiy-storchaka Can this be guaranteed in CPython? Wikipedia says size_t is at least 16 bit wise.

CuriousLearner added a commit to CuriousLearner/cpython that referenced this pull request

Aug 27, 2018

@CuriousLearner

@serhiy-storchaka

I think we can assume that size_t is at least 32 bit wise, and char is 8 bit wise. Many things in CPython implementation will be broken if this is not true.

@sir-sigurd

If I'm not mistaken, it can't overflow even if sizeof(Py_ssize_t) == 2 (presuming that CPython allocates at most PY_SSIZE_T_MAX bytes).

@zhangyangyu

If I'm not mistaken, it can't overflow even if sizeof(Py_ssize_t) == 2 (presuming that CPython allocates at most PY_SSIZE_T_MAX bytes).

I don't get it, how it's not possible.

And rechecking the patch it seems to me the overflow check is not right. You should check overflow before doing math operation. size_t alloc = ((size_t)self->num_marks << 1) + 20; might already overflowed and succeed in the overflow check in PyMem_Resize. What do you think about it? Am I mistaken something?

@sir-sigurd

@zhangyangyu
Presuming that CPython allocates at most PY_SSIZE_T_MAX bytes, we can assume that (size_t)self->num_marks <= PY_SSIZE_T_MAX / sizeof(Py_ssize_t). So
alloc <= (PY_SSIZE_T_MAX / sizeof(Py_ssize_t) << 1) + 20. If sizeof(Py_ssize_t) == 2, then alloc <= PY_SSIZE_T_MAX + 20 and it's obviously less than SIZE_MAX.

Makes sense?

@zhangyangyu