Skip to content

[2.7] bpo-36149 Fix potential use of uninitialized memory in cPickle #12105

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

Merged
merged 4 commits into from
Mar 4, 2019

Conversation

Yhg1s
Copy link
Member

@Yhg1s Yhg1s commented Feb 28, 2019

Fix potential use of uninitialized memory in cPickle when reading truncated pickles from FILE*s.

https://bugs.python.org/issue36149

Copy link
Contributor

@benjaminp benjaminp left a comment

Choose a reason for hiding this comment

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

This is exactly why you shouldn't put all your logic in the "condition" of one if-statement.

}
int newchar;
while (i < (self->buf_size - 1)) {
if (feof(self->fp))
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this redundant with the check on line 593?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know, is it? I can never remember the guaranteed semantics of FILE*s, feof() and functions that return EOF (which can signal error). Since the original function used this, I kept this it in.

Copy link
Contributor

Choose a reason for hiding this comment

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

My manpage says getc() returns EOF on end-of-file and error, so that's why I believe this is subsumed by the next call.

@@ -607,6 +609,10 @@ readline_file(Unpicklerobject *self, char **s)
self->buf = newbuf;
self->buf_size = bigger;
}
done:
Copy link
Contributor

Choose a reason for hiding this comment

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

I find it a little weird that this section is down here when the only way you can get to it is to jump out of a twice nested loop above.

Copy link
Member Author

Choose a reason for hiding this comment

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

In my mind the end of the function is the natural place to put this, because it makes clear that the outer loop doesn't end without this bit getting executed (barring error-returns).

goto done;
if ((newchar = getc(self->fp)) == EOF)
goto done;
self->buf[i++] = newchar;
Copy link
Contributor

Choose a reason for hiding this comment

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

self->buf[i++] = (newchar == EOF) ? '\0' : newchar maybe?

Copy link
Member Author

Choose a reason for hiding this comment

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

Didn't you just complain about too much logic in a single statement? :)

Copy link
Member Author

Choose a reason for hiding this comment

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

(I'm happy to change it if you prefer, but since this code is all blisfully gone in 3 and it isn't exactly performance-critical I'm not sure it really matters...)

@benjaminp
Copy link
Contributor

I think you could resolve all my nits by writing:

while (i < (self->buf_size - 1)) {
    int newchar = getc(self->fp);
    int done = newchar == EOF || newchar == '\n';
    if (done) {
        self->buf[i] = '\0';
        *s = self->buf;
        return i;
    }
    self->buf[i] = newchar;
    i++;
}

Do you think that's clearer?

@benjaminp
Copy link
Contributor

(You could even bring back the for loop.)

@Yhg1s
Copy link
Member Author

Yhg1s commented Mar 1, 2019

The loop you're proposing makes it not write the newline to self->buf. I'm not sure if that really matters (because it depends on what the callers of readline_file do) but it certainly changes what readline_file does.

@benjaminp
Copy link
Contributor

Ooops. Maybe

while (i < (self->buf_size - 1)) {
    int newchar = getc(self->fp);
    if (newchar != EOF) {
        self->buf[i++] = newchar;
    }
    if (newchar == EOF || newchar == '\n') {
        self->buf[i] = '\0';
        *s = self->buf;
        return i;
    }
}

then?

@Yhg1s
Copy link
Member Author

Yhg1s commented Mar 1, 2019

Fine, if you want to hide the function exit in the inner loop, there you go :)

Copy link
Contributor

@benjaminp benjaminp left a comment

Choose a reason for hiding this comment

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

We must just have different C aesthetics.

@Yhg1s Yhg1s merged commit d9bf7f4 into python:2.7 Mar 4, 2019
@bedevere-bot
Copy link

@Yhg1s: Please replace # with GH- in the commit message next time. Thanks!

@Yhg1s Yhg1s deleted the cpickle branch March 4, 2019 18:53
@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 Windows7 SP1 2.7 has failed when building commit d9bf7f4.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/all/#builders/68/builds/238) and take a look at the build logs.
  4. Check if the failure is related to this commit (d9bf7f4) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/all/#builders/68/builds/238

Click to see traceback logs
From https://github.com/python/cpython
 * branch            2.7        -> FETCH_HEAD
Reset branch '2.7'

Could Not Find C:\buildbot.python.org\2.7.kloth-win64\build\Lib\*.pyc
The system cannot find the file specified.

test test_urllibnet failed -- multiple errors occurred; run in verbose mode for details
Resource 'http://www.example.com' is not available
C:\buildbot.python.org\2.7.kloth-win64\build\lib\distutils\msvc9compiler.py:120: DeprecationWarning: decoding Unicode is not supported in 3.x
  s = dec("mbcs")
Resource 'ipv6.google.com' is not available
C:\buildbot.python.org\2.7.kloth-win64\build\lib\threading.py:846: DeprecationWarning: sys.exc_clear() not supported in 3.x; use except clauses
  self.__exc_clear()
'python2.4' is not recognized as an internal or external command,
operable program or batch file.
'python2.5' is not recognized as an internal or external command,
operable program or batch file.
'python2.6' is not recognized as an internal or external command,
operable program or batch file.
'python2.7' is not recognized as an internal or external command,
operable program or batch file.
    a DOS box should flash briefly ...
C:\buildbot.python.org\2.7.kloth-win64\build\lib\shutil.py:78: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  os.path.normcase(os.path.abspath(dst)))
C:\buildbot.python.org\2.7.kloth-win64\build\lib\multiprocessing\pool.py:117: DeprecationWarning: can't pickle function objects
  put((job, i, result))
C:\buildbot.python.org\2.7.kloth-win64\build\lib\multiprocessing\pool.py:117: DeprecationWarning: can't pickle function objects
  put((job, i, result))
Berkeley DB 4.7.25: (May 15, 2008)
Test path prefix:  c:\users\buildbot\appdata\local\temp\z-test_bsddb3-4576
XXX: timeout happened beforestartup was confirmed - see issue 3892
test test_urllibnet failed -- multiple errors occurred

@Yhg1s
Copy link
Member Author

Yhg1s commented Mar 4, 2019

FYI, the amd64 windows7 buidbot failure appears unrelated to this change (it's a test_urllibnet timeout failure).

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.

4 participants