Subject: [Boost-bugs] [Boost C++ Libraries] #6856: Critical problem with serialization and class versioning
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2012-05-02 15:07:59
#6856: Critical problem with serialization and class versioning
---------------------------------+------------------------------------------
Reporter: harris.pc@⦠| Owner: ramey
Type: Bugs | Status: new
Milestone: To Be Determined | Component: serialization
Version: Boost 1.49.0 | Severity: Problem
Keywords: |
---------------------------------+------------------------------------------
I am investigating strange error "input stream error" messages that I've
been getting for a while when loading XML archives,
and it turns out that boost::serialization is no longer enforcing strictly
incrementing versioning numbers.
Specifically the patch:
https://github.com/ryppl/boost-
svn/commit/66e85ade721a82bbc2e082749d6af2eefcb63dcb#boost/archive/detail
Which has the comment:
{{{
+ // note: we now comment this out. Before we permited archive
+ // version # to be very large. Now we don't. To permit
+ // readers of these old archives, we have to suppress this
+ // code. Perhaps in the future we might re-enable it but
+ // permit its suppression with a runtime switch.
}}}
I checked the documentation, and it is still assuming that class versions
always increment, and it is implied (to me) that if a class version in an
archive is larger than the BOOST_CLASS_VERSION() in the source, then it
will not load (and checking the code, looks like it should throw a
unsupported_class_version error).
This is a critical problem, and is now my likely candidate for the reason
why sometimes my programs crash when loading binary archives that are
written by a newer version of my program.
Basically, it NEVER checks if we are loading an archive that is newer than
what the program is supported. Â Thus, it will assume that it is reading
the correct-versioned archive and serialize the wrong data in the wrong
order.
I wrote a small program to demonstrate the problem, most of the code was
copied from the "bus" example in the documentation.
The Makefile compiles 4 different versions: Â xml and binary variants, each
that write either version=0, version=1 classes.
It is a program that loads an archive file (if the file can be opened),
and then writes an archive file.
The class written is a simple class with two integer variables.
In version=0, it writes var1.
In version=1, it writes var2, and then var1.
This is what it looks like when you run the programs:
{{{
$ ./test_version_binary_0
Could not open file to read: bus_route.binary
Saved, var1=1
------- so a binary archive is written to disk, version=0
$ ./test_version_binary_1
Restored, var1=1 var2=2
Saved, var1=1 var2=2
------- archive was loaded correctly, and then written back to disk, now
version=1
------- now lets try and run the version=0 program
$ ./test_version_binary_0
Restored, var1=2
FAIL! var1 should always be 1
$
------- archive was loaded, apparently without error, but inspecting the
data reveals that var1 was loaded with var2's value.
}}}
For the XML archive, we fortunately catch the problem, but only because it
was expecting a different set of xml-tags:
{{{
$ ./test_version_xml_0
Could not open file to read: bus_route.xml
Saved, var1=1
$ ./test_version_xml_1
Restored, var1=1 var2=2
Saved, var1=1 var2=2
$ ./test_version_xml_0
terminate called after throwing an instance of
'boost::archive::xml_archive_exception'
 what():  XML start/end tag mismatch - var1
Aborted
$
}}}
It seems to me that there are three solutions to this situation:
1) ignore problem. Â allow boost-based software to incorrectly load binary
archives.
2) change the documentation, tell users that they will need to manually
test the version passed to void serialize(Archive,int) methods, and throw
exceptions themselves if required.
That will also mean upgrading all of the existing software and adding a
test into every single serialize() function/method in existing production
code.
3) turn back on the version check that was turned off in the above patch.
This is what it looks like when I change the #if 0 to #if 1
{{{
$ ./test_version_binary_0
terminate called after throwing an instance of
'boost::archive::archive_exception'
 what():  class version 9bus_route
Aborted
}}}
And that's what I was expecting it to do.
-- Ticket URL: <https://svn.boost.org/trac/boost/ticket/6856> Boost C++ Libraries <http://www.boost.org/> Boost provides free peer-reviewed portable C++ source libraries.
This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:09 UTC