serialization: fix infinite loops and clean up dispatching

Resolves #8687
This commit is contained in:
jeffro256 2023-11-17 02:19:45 -06:00
parent 8eab181fe1
commit 33e3f72d24
No known key found for this signature in database
GPG Key ID: 6F79797A6E392442
9 changed files with 88 additions and 153 deletions

View File

@ -52,7 +52,7 @@ namespace cryptonote
// load // load
template <template <bool> class Archive> template <template <bool> class Archive>
bool do_serialize(Archive<false>& ar) bool member_do_serialize(Archive<false>& ar)
{ {
// size - 1 - because of variant tag // size - 1 - because of variant tag
for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size) for (size = 1; size <= TX_EXTRA_PADDING_MAX_COUNT; ++size)
@ -73,7 +73,7 @@ namespace cryptonote
// store // store
template <template <bool> class Archive> template <template <bool> class Archive>
bool do_serialize(Archive<true>& ar) bool member_do_serialize(Archive<true>& ar)
{ {
if(TX_EXTRA_PADDING_MAX_COUNT < size) if(TX_EXTRA_PADDING_MAX_COUNT < size)
return false; return false;
@ -129,7 +129,7 @@ namespace cryptonote
// load // load
template <template <bool> class Archive> template <template <bool> class Archive>
bool do_serialize(Archive<false>& ar) bool member_do_serialize(Archive<false>& ar)
{ {
std::string field; std::string field;
if(!::do_serialize(ar, field)) if(!::do_serialize(ar, field))
@ -142,7 +142,7 @@ namespace cryptonote
// store // store
template <template <bool> class Archive> template <template <bool> class Archive>
bool do_serialize(Archive<true>& ar) bool member_do_serialize(Archive<true>& ar)
{ {
std::ostringstream oss; std::ostringstream oss;
binary_archive<true> oar(oss); binary_archive<true> oar(oss);

View File

@ -42,7 +42,7 @@ namespace serialization
typename std::enable_if<!use_container_varint<T>(), bool>::type typename std::enable_if<!use_container_varint<T>(), bool>::type
serialize_container_element(Archive& ar, T& e) serialize_container_element(Archive& ar, T& e)
{ {
return ::do_serialize(ar, e); return do_serialize(ar, e);
} }
template<typename Archive, typename T> template<typename Archive, typename T>
@ -52,7 +52,7 @@ namespace serialization
static constexpr const bool previously_varint = std::is_same<uint64_t, T>() || std::is_same<uint32_t, T>(); static constexpr const bool previously_varint = std::is_same<uint64_t, T>() || std::is_same<uint32_t, T>();
if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving()) if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving())
return ::do_serialize(ar, e); return do_serialize(ar, e);
ar.serialize_varint(e); ar.serialize_varint(e);
return true; return true;
} }

View File

@ -42,14 +42,12 @@ struct debug_archive : public json_archive<W> {
}; };
template <class T> template <class T>
struct serializer<debug_archive<true>, T> static inline bool do_serialize(debug_archive<true> &ar, T &v)
{ {
static void serialize(debug_archive<true> &ar, T &v)
{
ar.begin_object(); ar.begin_object();
ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag()); ar.tag(variant_serialization_traits<debug_archive<true>, T>::get_tag());
serializer<json_archive<true>, T>::serialize(ar, v); do_serialize(static_cast<json_archive<true>&>(ar), v);
ar.end_object(); ar.end_object();
ar.stream() << std::endl; ar.stream() << std::endl;
} return true;
}; }

View File

@ -31,8 +31,6 @@
#include "cryptonote_basic/difficulty.h" #include "cryptonote_basic/difficulty.h"
#include "serialization.h" #include "serialization.h"
template<> struct is_basic_type<cryptonote::difficulty_type> { typedef boost::true_type type; };
template <template <bool> class Archive> template <template <bool> class Archive>
inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff) inline bool do_serialize(Archive<false>& ar, cryptonote::difficulty_type &diff)
{ {

View File

@ -47,7 +47,7 @@ namespace serialization
typename std::enable_if<!use_pair_varint<T>(), bool>::type typename std::enable_if<!use_pair_varint<T>(), bool>::type
serialize_pair_element(Archive& ar, T& e) serialize_pair_element(Archive& ar, T& e)
{ {
return ::do_serialize(ar, e); return do_serialize(ar, e);
} }
template<typename Archive, typename T> template<typename Archive, typename T>
@ -57,7 +57,7 @@ namespace serialization
static constexpr const bool previously_varint = std::is_same<uint64_t, T>(); static constexpr const bool previously_varint = std::is_same<uint64_t, T>();
if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving()) if (!previously_varint && ar.varint_bug_backward_compatibility_enabled() && !typename Archive::is_saving())
return ::do_serialize(ar, e); return do_serialize(ar, e);
ar.serialize_varint(e); ar.serialize_varint(e);
return true; return true;
} }

View File

@ -57,73 +57,30 @@
template <class T> template <class T>
struct is_blob_type { typedef boost::false_type type; }; struct is_blob_type { typedef boost::false_type type; };
/*! \struct has_free_serializer
*
* \brief a descriptor for dispatching serialize
*/
template <class T>
struct has_free_serializer { typedef boost::true_type type; };
/*! \struct is_basic_type
*
* \brief a descriptor for dispatching serialize
*/
template <class T>
struct is_basic_type { typedef boost::false_type type; };
template<typename F, typename S>
struct is_basic_type<std::pair<F,S>> { typedef boost::true_type type; };
template<>
struct is_basic_type<std::string> { typedef boost::true_type type; };
/*! \struct serializer
*
* \brief ... wouldn't a class be better?
*
* \detailed The logic behind serializing data. Places the archive
* data into the supplied parameter. This dispatches based on the
* supplied \a T template parameter's traits of is_blob_type or it is
* an integral (as defined by the is_integral trait). Depends on the
* \a Archive parameter to have overloaded the serialize_blob(T v,
* size_t size) and serialize_int(T v) base on which trait it
* applied. When the class has neither types, it falls to the
* overloaded method do_serialize(Archive ar) in T to do the work.
*/
template <class Archive, class T>
struct serializer{
static bool serialize(Archive &ar, T &v) {
return serialize(ar, v, typename boost::is_integral<T>::type(), typename is_blob_type<T>::type(), typename is_basic_type<T>::type());
}
template<typename A>
static bool serialize(Archive &ar, T &v, boost::false_type, boost::true_type, A a) {
ar.serialize_blob(&v, sizeof(v));
return true;
}
template<typename A>
static bool serialize(Archive &ar, T &v, boost::true_type, boost::false_type, A a) {
ar.serialize_int(v);
return true;
}
static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::false_type) {
//serialize_custom(ar, v, typename has_free_serializer<T>::type());
return v.do_serialize(ar);
}
static bool serialize(Archive &ar, T &v, boost::false_type, boost::false_type, boost::true_type) {
//serialize_custom(ar, v, typename has_free_serializer<T>::type());
return do_serialize(ar, v);
}
static void serialize_custom(Archive &ar, T &v, boost::true_type) {
}
};
/*! \fn do_serialize(Archive &ar, T &v) /*! \fn do_serialize(Archive &ar, T &v)
* *
* \brief just calls the serialize function defined for ar and v... * \brief main function for dispatching serialization for a given pair of archive and value types
*
* Types marked true with is_blob_type<T> will be serialized as a blob, integral types will be
* serialized as integers, and types who have a `member_do_serialize` method will be serialized
* using that method. Booleans are serialized like blobs.
*/ */
template <class Archive, class T> template <class Archive, class T>
inline bool do_serialize(Archive &ar, T &v) inline std::enable_if_t<is_blob_type<T>::type::value, bool> do_serialize(Archive &ar, T &v)
{ {
return ::serializer<Archive, T>::serialize(ar, v); ar.serialize_blob(&v, sizeof(v));
return true;
}
template <class Archive, class T>
inline std::enable_if_t<boost::is_integral<T>::value, bool> do_serialize(Archive &ar, T &v)
{
ar.serialize_int(v);
return true;
}
template <class Archive, class T>
inline auto do_serialize(Archive &ar, T &v) -> decltype(v.member_do_serialize(ar), true)
{
return v.member_do_serialize(ar);
} }
template <class Archive> template <class Archive>
inline bool do_serialize(Archive &ar, bool &v) inline bool do_serialize(Archive &ar, bool &v)
@ -144,16 +101,6 @@ inline bool do_serialize(Archive &ar, bool &v)
typedef boost::true_type type; \ typedef boost::true_type type; \
} }
/*! \macro FREE_SERIALIZER
*
* \brief adds the has_free_serializer to the type
*/
#define FREE_SERIALIZER(T) \
template<> \
struct has_free_serializer<T> { \
typedef boost::true_type type; \
}
/*! \macro VARIANT_TAG /*! \macro VARIANT_TAG
* *
* \brief Adds the tag \tag to the \a Archive of \a Type * \brief Adds the tag \tag to the \a Archive of \a Type
@ -174,7 +121,7 @@ inline bool do_serialize(Archive &ar, bool &v)
*/ */
#define BEGIN_SERIALIZE() \ #define BEGIN_SERIALIZE() \
template <bool W, template <bool> class Archive> \ template <bool W, template <bool> class Archive> \
bool do_serialize(Archive<W> &ar) { bool member_do_serialize(Archive<W> &ar) {
/*! \macro BEGIN_SERIALIZE_OBJECT /*! \macro BEGIN_SERIALIZE_OBJECT
* *
@ -183,7 +130,7 @@ inline bool do_serialize(Archive &ar, bool &v)
*/ */
#define BEGIN_SERIALIZE_OBJECT() \ #define BEGIN_SERIALIZE_OBJECT() \
template <bool W, template <bool> class Archive> \ template <bool W, template <bool> class Archive> \
bool do_serialize(Archive<W> &ar) { \ bool member_do_serialize(Archive<W> &ar) { \
ar.begin_object(); \ ar.begin_object(); \
bool r = do_serialize_object(ar); \ bool r = do_serialize_object(ar); \
ar.end_object(); \ ar.end_object(); \
@ -197,11 +144,6 @@ inline bool do_serialize(Archive &ar, bool &v)
#define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \ #define PREPARE_CUSTOM_VECTOR_SERIALIZATION(size, vec) \
::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving()) ::serialization::detail::prepare_custom_vector_serialization(size, vec, typename Archive<W>::is_saving())
/*! \macro PREPARE_CUSTOM_DEQUE_SERIALIZATION
*/
#define PREPARE_CUSTOM_DEQUE_SERIALIZATION(size, vec) \
::serialization::detail::prepare_custom_deque_serialization(size, vec, typename Archive<W>::is_saving())
/*! \macro END_SERIALIZE /*! \macro END_SERIALIZE
* \brief self-explanatory * \brief self-explanatory
*/ */
@ -209,16 +151,6 @@ inline bool do_serialize(Archive &ar, bool &v)
return ar.good(); \ return ar.good(); \
} }
/*! \macro VALUE(f)
* \brief the same as FIELD(f)
*/
#define VALUE(f) \
do { \
ar.tag(#f); \
bool r = ::do_serialize(ar, f); \
if (!r || !ar.good()) return false; \
} while(0);
/*! \macro FIELD_N(t,f) /*! \macro FIELD_N(t,f)
* *
* \brief serializes a field \a f tagged \a t * \brief serializes a field \a f tagged \a t
@ -226,7 +158,7 @@ inline bool do_serialize(Archive &ar, bool &v)
#define FIELD_N(t, f) \ #define FIELD_N(t, f) \
do { \ do { \
ar.tag(t); \ ar.tag(t); \
bool r = ::do_serialize(ar, f); \ bool r = do_serialize(ar, f); \
if (!r || !ar.good()) return false; \ if (!r || !ar.good()) return false; \
} while(0); } while(0);
@ -237,7 +169,7 @@ inline bool do_serialize(Archive &ar, bool &v)
#define FIELD(f) \ #define FIELD(f) \
do { \ do { \
ar.tag(#f); \ ar.tag(#f); \
bool r = ::do_serialize(ar, f); \ bool r = do_serialize(ar, f); \
if (!r || !ar.good()) return false; \ if (!r || !ar.good()) return false; \
} while(0); } while(0);
@ -247,7 +179,7 @@ inline bool do_serialize(Archive &ar, bool &v)
*/ */
#define FIELDS(f) \ #define FIELDS(f) \
do { \ do { \
bool r = ::do_serialize(ar, f); \ bool r = do_serialize(ar, f); \
if (!r || !ar.good()) return false; \ if (!r || !ar.good()) return false; \
} while(0); } while(0);
@ -317,17 +249,6 @@ namespace serialization {
vec.resize(size); vec.resize(size);
} }
template <typename T>
void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<true>& /*is_saving*/)
{
}
template <typename T>
void prepare_custom_deque_serialization(size_t size, std::deque<T>& vec, const boost::mpl::bool_<false>& /*is_saving*/)
{
vec.resize(size);
}
/*! \fn do_check_stream_state /*! \fn do_check_stream_state
* *
* \brief self explanatory * \brief self explanatory

View File

@ -39,7 +39,7 @@ namespace serialization
template <typename Archive, class T> template <typename Archive, class T>
bool serialize_tuple_element(Archive& ar, T& e) bool serialize_tuple_element(Archive& ar, T& e)
{ {
return ::do_serialize(ar, e); return do_serialize(ar, e);
} }
template <typename Archive> template <typename Archive>

View File

@ -72,7 +72,7 @@ struct variant_reader
{ {
if(variant_serialization_traits<Archive, current_type>::get_tag() == t) { if(variant_serialization_traits<Archive, current_type>::get_tag() == t) {
current_type x; current_type x;
if(!::do_serialize(ar, x)) if(!do_serialize(ar, x))
{ {
ar.set_fail(); ar.set_fail();
return false; return false;
@ -100,19 +100,13 @@ struct variant_reader<Archive, Variant, TBegin, TBegin>
} }
}; };
template <template <bool> class Archive, typename... T>
template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> static bool do_serialize(Archive<false> &ar, boost::variant<T...> &v) {
struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> using types = typename boost::variant<T...>::types;
{ typename Archive<false>::variant_tag_type t;
typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type;
typedef typename Archive<false>::variant_tag_type variant_tag_type;
typedef typename variant_type::types types;
static bool serialize(Archive<false> &ar, variant_type &v) {
variant_tag_type t;
ar.begin_variant(); ar.begin_variant();
ar.read_variant_tag(t); ar.read_variant_tag(t);
if(!variant_reader<Archive<false>, variant_type, if(!variant_reader<Archive<false>, boost::variant<T...>,
typename boost::mpl::begin<types>::type, typename boost::mpl::begin<types>::type,
typename boost::mpl::end<types>::type>::read(ar, v, t)) typename boost::mpl::end<types>::type>::read(ar, v, t))
{ {
@ -121,27 +115,21 @@ struct serializer<Archive<false>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
} }
ar.end_variant(); ar.end_variant();
return true; return true;
} }
};
template <template <bool> class Archive, BOOST_VARIANT_ENUM_PARAMS(typename T)> template <template <bool> class Archive>
struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>> struct variant_write_visitor : public boost::static_visitor<bool>
{ {
typedef boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> variant_type;
//typedef typename Archive<true>::variant_tag_type variant_tag_type;
struct visitor : public boost::static_visitor<bool>
{
Archive<true> &ar; Archive<true> &ar;
visitor(Archive<true> &a) : ar(a) { } variant_write_visitor(Archive<true> &a) : ar(a) { }
template <class T> template <class T>
bool operator ()(T &rv) const bool operator ()(T &rv) const
{ {
ar.begin_variant(); ar.begin_variant();
ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag()); ar.write_variant_tag(variant_serialization_traits<Archive<true>, T>::get_tag());
if(!::do_serialize(ar, rv)) if(!do_serialize(ar, rv))
{ {
ar.set_fail(); ar.set_fail();
return false; return false;
@ -149,9 +137,10 @@ struct serializer<Archive<true>, boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)>>
ar.end_variant(); ar.end_variant();
return true; return true;
} }
};
static bool serialize(Archive<true> &ar, variant_type &v) {
return boost::apply_visitor(visitor(ar), v);
}
}; };
template <template <bool> class Archive, typename... T>
static bool do_serialize(Archive<true> &ar, boost::variant<T...> &v)
{
return boost::apply_visitor(variant_write_visitor<Archive>(ar), v);
}

View File

@ -59,9 +59,7 @@ struct Struct
}; };
template <class Archive> template <class Archive>
struct serializer<Archive, Struct> static bool do_serialize(Archive &ar, Struct &s) {
{
static bool serialize(Archive &ar, Struct &s) {
ar.begin_object(); ar.begin_object();
ar.tag("a"); ar.tag("a");
ar.serialize_int(s.a); ar.serialize_int(s.a);
@ -71,8 +69,7 @@ struct serializer<Archive, Struct>
ar.serialize_blob(s.blob, sizeof(s.blob)); ar.serialize_blob(s.blob, sizeof(s.blob));
ar.end_object(); ar.end_object();
return true; return true;
} }
};
struct Struct1 struct Struct1
{ {
@ -122,6 +119,23 @@ bool try_parse(const string &blob)
return serialization::parse_binary(blob, s1); return serialization::parse_binary(blob, s1);
} }
namespace example_namespace
{
struct ADLExampleStruct
{
std::string msg;
};
template <class Archive>
static bool do_serialize(Archive &ar, ADLExampleStruct &aes)
{
ar.begin_object();
FIELD_N("custom_fieldname", aes.msg);
ar.end_object();
return ar.good();
}
}
TEST(Serialization, BinaryArchiveInts) { TEST(Serialization, BinaryArchiveInts) {
uint64_t x = 0xff00000000, x1; uint64_t x = 0xff00000000, x1;
@ -1178,3 +1192,18 @@ TEST(Serialization, difficulty_type)
ASSERT_EQ(v_original, v_unserialized); ASSERT_EQ(v_original, v_unserialized);
} }
TEST(Serialization, adl_free_function)
{
std::stringstream ss;
json_archive<true> ar(ss);
const std::string msg = "Howdy, World!";
example_namespace::ADLExampleStruct aes{msg};
ASSERT_TRUE(serialization::serialize(ar, aes));
// VVVVVVVVVVVVVVVVVVVVVVVVVV weird string serialization artifact
const std::string expected = "{\"custom_fieldname\": " + std::to_string(msg.size()) + '"' + epee::string_tools::buff_to_hex_nodelimer(msg) + "\"}";
EXPECT_EQ(expected, ss.str());
}