From 3ccfbfaba722d068bce535225979c13ad0e19fd1 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 9 Jan 2019 22:56:40 +0100 Subject: [PATCH] putil: fix hang reading bam due to macOS libc++ bug in istream::eof() Apparently eof() in older versions of libc++ returns true if the last character has just been read, whereas the proper behavior is only to return true when attempting to read past the end of the stream. Since failbit is reliably set in all cases when reading past the length of the stream, we should only trust the result of eof() if fail() also returns true. --- panda/src/putil/datagramInputFile.cxx | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/panda/src/putil/datagramInputFile.cxx b/panda/src/putil/datagramInputFile.cxx index c4802ceed2..600f699635 100644 --- a/panda/src/putil/datagramInputFile.cxx +++ b/panda/src/putil/datagramInputFile.cxx @@ -109,7 +109,7 @@ read_header(std::string &header, size_t num_bytes) { nassertr(buffer != nullptr, false); _in->read(buffer, num_bytes); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { return false; } @@ -130,7 +130,7 @@ get_datagram(Datagram &data) { // First, get the size of the upcoming datagram. StreamReader reader(_in, false); uint32_t num_bytes_32 = reader.get_uint32(); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { return false; } @@ -141,16 +141,29 @@ get_datagram(Datagram &data) { return true; } + if (_in->eof()) { + return false; + } + size_t num_bytes = (size_t)num_bytes_32; if (num_bytes_32 == (uint32_t)-1) { // Another special case for a value larger than 32 bits. uint64_t num_bytes_64 = reader.get_uint64(); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { _error = true; return false; } + if (num_bytes_64 == 0) { + data.clear(); + return false; + } + + if (_in->eof()) { + return false; + } + num_bytes = (size_t)num_bytes_64; // Make sure we have a reasonable datagram size for putting into memory. @@ -169,6 +182,11 @@ get_datagram(Datagram &data) { while (bytes_read < num_bytes) { size_t bytes_left = num_bytes - bytes_read; + if (_in->eof()) { + _error = true; + return false; + } + // Hold up a second - datagrams >4MB are pretty large by bam/network // standards. Let's take it 4MB at a time just in case the length is // corrupt, so we don't allocate potentially a few GBs of RAM only to @@ -180,7 +198,7 @@ get_datagram(Datagram &data) { unsigned char *ptr = &buffer.p()[bytes_read]; _in->read((char *)ptr, (streamsize)bytes_left); - if (_in->fail() || _in->eof()) { + if (_in->fail()) { _error = true; return false; } @@ -210,7 +228,7 @@ save_datagram(SubfileInfo &info) { // First, get the size of the upcoming datagram. StreamReader reader(_in, false); size_t num_bytes_32 = reader.get_uint32(); - if (_in->fail() || _in->eof()) { + if (_in->fail() || (_in->eof() && num_bytes_32 > 0)) { return false; }