summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorarseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640>2010-07-22 05:09:25 +0000
committerarseny.kapoulkine <arseny.kapoulkine@99668b35-9821-0410-8761-19e4c4f06640>2010-07-22 05:09:25 +0000
commit04085a8875d30cb39ccfd0e99b3d0aa098f17d6d (patch)
treed1f2f78c1e88be814c4cac969769ca02592137fa /src
parentb4a012a4d5bf2ab4da5a59f448095d18f04a77b0 (diff)
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
Diffstat (limited to 'src')
-rw-r--r--src/pugixml.cpp56
1 files changed, 38 insertions, 18 deletions
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 <typename T> xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T, std::char_traits<T> >& 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 <typename T> xml_parse_result load_stream_impl(xml_document& doc, std::basic_istream<T>& 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<T>::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<size_t>(length);
- T* s = static_cast<T*>(global_allocate((read_length > 0 ? read_length : 1) * sizeof(T)));
- if (!s) return make_parse_result(status_out_of_memory);
+ if (static_cast<std::streamsize>(read_length) != length || length < 0) return make_parse_result(status_out_of_memory);
- stream.read(s, static_cast<std::streamsize>(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<size_t>(stream.gcount());
- assert(actual_length <= read_length);
+ stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(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<size_t>(stream.gcount());
+ assert(actual_length <= read_length);
+
+ return doc.load_buffer_inplace_own(buffer.release(), actual_length * sizeof(T), options, encoding);
}
#endif
}