From 04085a8875d30cb39ccfd0e99b3d0aa098f17d6d Mon Sep 17 00:00:00 2001 From: "arseny.kapoulkine" Date: Thu, 22 Jul 2010 05:09:25 +0000 Subject: Fixed stream loading memory leaks in the unlikely case streams have exception mask set, better stream error handling. git-svn-id: http://pugixml.googlecode.com/svn/trunk@610 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 56 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 7f36ada..3f37e65 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -2968,38 +2968,58 @@ namespace } #ifndef PUGIXML_NO_STL - template xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + struct buffer_holder { - if (!stream.good()) return make_parse_result(status_io_error); + void* data; + + buffer_holder(void* data): data(data) + { + } + + ~buffer_holder() + { + if (data) global_deallocate(data); + } + + void* release() + { + void* result = data; + data = 0; + return result; + } + }; + + template xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding) + { + if (stream.fail()) return make_parse_result(status_io_error); // get length of remaining data in stream - std::streamoff pos = stream.tellg(); + typename std::basic_istream::pos_type pos = stream.tellg(); stream.seekg(0, std::ios::end); std::streamoff length = stream.tellg() - pos; - stream.seekg(pos, std::ios::beg); + stream.seekg(pos); - if (!stream.good() || pos < 0 || length < 0) return make_parse_result(status_io_error); + if (stream.fail() || pos < 0) return make_parse_result(status_io_error); - // read stream data into memory + // guard against huge files size_t read_length = static_cast(length); - T* s = static_cast(global_allocate((read_length > 0 ? read_length : 1) * sizeof(T))); - if (!s) return make_parse_result(status_out_of_memory); + if (static_cast(read_length) != length || length < 0) return make_parse_result(status_out_of_memory); - stream.read(s, static_cast(read_length)); + // read stream data into memory (guard against stream exceptions with buffer holder) + buffer_holder buffer = global_allocate((read_length > 0 ? read_length : 1) * sizeof(T)); + if (!buffer.data) return make_parse_result(status_out_of_memory); - // check for errors - size_t actual_length = static_cast(stream.gcount()); - assert(actual_length <= read_length); + stream.read(static_cast(buffer.data), static_cast(read_length)); - if (read_length > 0 && actual_length == 0) - { - global_deallocate(s); - return make_parse_result(status_io_error); - } + // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors + if (stream.bad()) return make_parse_result(status_io_error); // load data from buffer - return doc.load_buffer_inplace_own(s, actual_length * sizeof(T), options, encoding); + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + return doc.load_buffer_inplace_own(buffer.release(), actual_length * sizeof(T), options, encoding); } #endif } -- cgit v1.2.3