mirror of
https://github.com/boostorg/serialization.git
synced 2026-07-21 13:33:32 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 432ef2ee5a | |||
| db2610e1e6 | |||
| 64c2f11a66 | |||
| f45573ed35 | |||
| c4ae6ec9d9 | |||
| 28f606fa7d | |||
| 22ae0cd043 |
@@ -7,6 +7,7 @@ on:
|
||||
- master
|
||||
- develop
|
||||
- feature/**
|
||||
merge_group:
|
||||
|
||||
env:
|
||||
UBSAN_OPTIONS: print_stacktrace=1
|
||||
|
||||
@@ -189,7 +189,14 @@ xml_iarchive_impl<Archive>::~xml_iarchive_impl(){
|
||||
if(boost::core::uncaught_exceptions() > 0)
|
||||
return;
|
||||
if(0 == (this->get_flags() & no_header)){
|
||||
gimpl->windup(is);
|
||||
// windup() parses the trailing end tag; an exception must not escape
|
||||
// this (implicitly noexcept) destructor and terminate the process.
|
||||
// A stream error while consuming the trailer is not worth that. #99
|
||||
BOOST_TRY {
|
||||
gimpl->windup(is);
|
||||
}
|
||||
BOOST_CATCH(...) {}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
}
|
||||
} // namespace archive
|
||||
|
||||
@@ -177,7 +177,14 @@ xml_wiarchive_impl<Archive>::~xml_wiarchive_impl(){
|
||||
if(boost::core::uncaught_exceptions() > 0)
|
||||
return;
|
||||
if(0 == (this->get_flags() & no_header)){
|
||||
gimpl->windup(is);
|
||||
// windup() parses the trailing end tag; an exception must not escape
|
||||
// this (implicitly noexcept) destructor and terminate the process.
|
||||
// A stream error while consuming the trailer is not worth that. #99
|
||||
BOOST_TRY {
|
||||
gimpl->windup(is);
|
||||
}
|
||||
BOOST_CATCH(...) {}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <optional>
|
||||
#endif
|
||||
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/serialization/item_version_type.hpp>
|
||||
#include <boost/serialization/library_version_type.hpp>
|
||||
#include <boost/serialization/version.hpp>
|
||||
@@ -83,7 +84,7 @@ void load_impl(
|
||||
}
|
||||
typename OT::value_type t;
|
||||
ar >> boost::serialization::make_nvp("value",t);
|
||||
ot = t;
|
||||
ot = boost::move(t);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -191,6 +191,15 @@ bool basic_xml_grammar<CharType>::my_parse(
|
||||
for(;;){
|
||||
CharType result;
|
||||
is.get(result);
|
||||
// Reaching end of input while scanning for the next character is the
|
||||
// normal way an archive ends (e.g. windup() consuming the trailer);
|
||||
// it is not a stream error. get() sets both eofbit and failbit at end
|
||||
// of stream, so test eof() *before* fail() -- otherwise the normal
|
||||
// termination is misreported as input_stream_error, which is fatal
|
||||
// when it surfaces in the (noexcept) archive destructor via windup().
|
||||
// See #99.
|
||||
if(is.eof())
|
||||
return false;
|
||||
if(is.fail()){
|
||||
boost::serialization::throw_exception(
|
||||
boost::archive::archive_exception(
|
||||
@@ -199,8 +208,6 @@ bool basic_xml_grammar<CharType>::my_parse(
|
||||
)
|
||||
);
|
||||
}
|
||||
if(is.eof())
|
||||
return false;
|
||||
arg += result;
|
||||
if(result == delimiter)
|
||||
break;
|
||||
|
||||
@@ -151,6 +151,8 @@ if ! $(BOOST_ARCHIVE_LIST) {
|
||||
[ test-bsl-run test_private_ctor ]
|
||||
[ test-bsl-run test_reset_object_address : A ]
|
||||
[ test-bsl-run test_void_cast ]
|
||||
[ test-bsl-run test_xml_trailing_whitespace ]
|
||||
[ test-bsl-run test_xml_missing_nvp ]
|
||||
[ test-bsl-run test_mult_archive_types : : : [ requires std_wstreambuf ] ]
|
||||
[ test-bsl-run test_iterators : : : [ requires std_wstreambuf ] ]
|
||||
[ test-bsl-run test_iterators_base64 ]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// test_optional.cpp
|
||||
|
||||
// (C) Copyright 2004 Pavel Vozenilek
|
||||
// Copyright 2026 Gennaro Prota
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -92,6 +93,59 @@ int test(){
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// A move-only value type: deleted copy, defaulted move. Loading an
|
||||
// optional<M> must move the deserialized value into place rather than copy
|
||||
// it.
|
||||
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) \
|
||||
&& !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS) \
|
||||
&& !defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||
#define BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY
|
||||
|
||||
struct M {
|
||||
int m_x;
|
||||
M() : m_x(0) {}
|
||||
explicit M(int x) : m_x(x) {}
|
||||
M(const M &) = delete;
|
||||
M & operator=(const M &) = delete;
|
||||
M(M &&) = default;
|
||||
M & operator=(M &&) = default;
|
||||
template<class Archive>
|
||||
void serialize(Archive & ar, const unsigned int /* version */){
|
||||
ar & boost::serialization::make_nvp("x", m_x);
|
||||
}
|
||||
};
|
||||
|
||||
template<template<class> class Optional>
|
||||
int test_move_only(){
|
||||
const char * testfile = boost::archive::tmpnam(NULL);
|
||||
BOOST_REQUIRE(NULL != testfile);
|
||||
|
||||
Optional<M> o_empty;
|
||||
Optional<M> o_value(M(42));
|
||||
{
|
||||
test_ostream os(testfile, TEST_STREAM_FLAGS);
|
||||
test_oarchive oa(os, TEST_ARCHIVE_FLAGS);
|
||||
oa << boost::serialization::make_nvp("o_empty", o_empty);
|
||||
oa << boost::serialization::make_nvp("o_value", o_value);
|
||||
}
|
||||
// start each target in the opposite state, so load must both reset and
|
||||
// assign
|
||||
Optional<M> o_empty_a(M(7));
|
||||
Optional<M> o_value_a;
|
||||
{
|
||||
test_istream is(testfile, TEST_STREAM_FLAGS);
|
||||
test_iarchive ia(is, TEST_ARCHIVE_FLAGS);
|
||||
ia >> boost::serialization::make_nvp("o_empty", o_empty_a);
|
||||
ia >> boost::serialization::make_nvp("o_value", o_value_a);
|
||||
}
|
||||
BOOST_CHECK(! o_empty_a);
|
||||
BOOST_CHECK(static_cast<bool>(o_value_a) && 42 == o_value_a->m_x);
|
||||
|
||||
std::remove(testfile);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif // move-only support available
|
||||
|
||||
#include <boost/serialization/optional.hpp>
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
#include <optional>
|
||||
@@ -102,5 +156,11 @@ int test_main( int /* argc */, char* /* argv */[] ){
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
test<std::optional>();
|
||||
#endif
|
||||
#ifdef BOOST_SERIALIZATION_TEST_OPTIONAL_MOVE_ONLY
|
||||
test_move_only<boost::optional>();
|
||||
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
|
||||
test_move_only<std::optional>();
|
||||
#endif
|
||||
#endif
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// test_xml_missing_nvp.cpp
|
||||
|
||||
// Copyright 2026 Gennaro Prota
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// Regression test for issue #109. Attempting to read an nvp that is not
|
||||
// present throws archive_exception, which the caller may catch. The failed
|
||||
// read consumes the closing tag, so at destruction windup() reaches end of
|
||||
// input -- the same Boost 1.66 my_parse regression as #82/#99, reached by a
|
||||
// different path. The input archive must still destruct without terminating.
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/archive/xml_oarchive.hpp>
|
||||
#include <boost/archive/xml_iarchive.hpp>
|
||||
#include <boost/archive/archive_exception.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
#include "test_tools.hpp"
|
||||
|
||||
int test_main(int /* argc */, char * /* argv */ []){
|
||||
// Build a valid XML archive holding a single value.
|
||||
std::string content;
|
||||
{
|
||||
std::ostringstream os;
|
||||
{
|
||||
boost::archive::xml_oarchive oa(os);
|
||||
const int x = 42;
|
||||
oa << boost::serialization::make_nvp("x", x);
|
||||
}
|
||||
content = os.str();
|
||||
}
|
||||
|
||||
int x = 0;
|
||||
bool caught = false;
|
||||
{
|
||||
std::istringstream is(content);
|
||||
boost::archive::xml_iarchive ia(is);
|
||||
ia >> boost::serialization::make_nvp("x", x);
|
||||
try {
|
||||
int y = 0;
|
||||
ia >> boost::serialization::make_nvp("not_there", y);
|
||||
} catch (const boost::archive::archive_exception &){
|
||||
caught = true;
|
||||
}
|
||||
// `ia` is destroyed here, after the caught exception. Before the fix,
|
||||
// windup() hit end of input (the failed read consumed the closing
|
||||
// tag) and threw out of the noexcept destructor, terminating.
|
||||
}
|
||||
BOOST_CHECK(42 == x);
|
||||
BOOST_CHECK(caught);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
|
||||
// test_xml_trailing_whitespace.cpp
|
||||
|
||||
// Copyright 2026 Gennaro Prota
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// Regression test for issue #99. When an XML input archive is destroyed and
|
||||
// its stream has no closing tag left -- only trailing whitespace before end
|
||||
// of input (a truncated archive; the reported case had the stream "contain
|
||||
// \r\n") -- windup() reaches end of input while scanning for the trailing
|
||||
// tag. End of input there is normal termination, not a stream error, so the
|
||||
// (implicitly noexcept) destructor must complete cleanly rather than throw,
|
||||
// which used to terminate the process.
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/archive/xml_oarchive.hpp>
|
||||
#include <boost/archive/xml_iarchive.hpp>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
|
||||
#include "test_tools.hpp"
|
||||
|
||||
int test_main(int /* argc */, char * /* argv */ []){
|
||||
// Build a valid XML archive holding a single value.
|
||||
std::string content;
|
||||
{
|
||||
std::ostringstream os;
|
||||
{
|
||||
boost::archive::xml_oarchive oa(os);
|
||||
const int x = 42;
|
||||
oa << boost::serialization::make_nvp("x", x);
|
||||
}
|
||||
content = os.str();
|
||||
}
|
||||
|
||||
// Drop the closing document tag and leave only trailing whitespace, so the
|
||||
// input archive's windup() reaches end of input at destruction instead of
|
||||
// finding a tag.
|
||||
const std::string::size_type close = content.rfind("</boost_serialization>");
|
||||
BOOST_REQUIRE(std::string::npos != close);
|
||||
content.erase(close);
|
||||
content += "\r\n";
|
||||
|
||||
int y = 0;
|
||||
{
|
||||
std::istringstream is(content);
|
||||
boost::archive::xml_iarchive ia(is);
|
||||
ia >> boost::serialization::make_nvp("x", y);
|
||||
// `ia` is destroyed here with only trailing whitespace left. Before
|
||||
// the fix, windup() threw input_stream_error out of the noexcept
|
||||
// destructor and terminated the process.
|
||||
}
|
||||
BOOST_CHECK(42 == y);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user