diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 3b5d8dd016..8372636769 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -18,6 +18,7 @@ option(BENCHMARKS "Build benchmarks" ON) option(FUZZING "Build fuzzing harnesses" OFF) option(DISABLE_TBB "Intel Thread Building Blocks" ON) option(COVERAGE "Enable collecting coverage from tests" OFF) +option(SERIALIZE_CANARY "Build with serialize canary" OFF) option(ENABLE_HEAVY_TESTS "Enable heavy tests when collecting coverage" OFF) option(INSTALL_BARRETENBERG "Enable installation of barretenberg. (Projects embedding barretenberg may want to turn this OFF.)" ON) @@ -31,6 +32,10 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "a set(DISABLE_TBB 0) endif() +if(SERIALIZE_CANARY) + add_definitions(-DENABLE_SERIALIZE_CANARY) +endif() + if(FUZZING) add_definitions(-DFUZZING=1) diff --git a/cpp/src/barretenberg/common/serialize.hpp b/cpp/src/barretenberg/common/serialize.hpp index 30f99bd949..622dc919f7 100644 --- a/cpp/src/barretenberg/common/serialize.hpp +++ b/cpp/src/barretenberg/common/serialize.hpp @@ -27,6 +27,7 @@ * - to_buffer */ #pragma once +#include #include #include "barretenberg/common/net.hpp" #include @@ -149,6 +150,24 @@ template inline std::enable_if_t> write(std:: write(ptr, value); os.write((char*)buf.data(), sizeof(T)); } + +// DEBUG_CANARY_READ and DEBUG_CANARY_WRITE write strings during debug testing +// so that we can detect serialization misalignment for more complicated types. +// This is in an awkward location as it must see the above functions, and be seen by the below functions. +#ifndef ENABLE_SERIALIZE_CANARY +#define DEBUG_CANARY_WRITE(buf, x) +#define DEBUG_CANARY_READ(it, x) +#else +#define DEBUG_CANARY_WRITE(buf, x) serialize::write(buf, (uint64_t) typeid(x).hash_code()) +#define DEBUG_CANARY_READ(it, x) \ + { \ + uint64_t hash_code; \ + serialize::read(it, hash_code); \ + if (hash_code != (uint64_t) typeid(x).hash_code()) { \ + throw std::runtime_error(std::string("Could not read magic string for ") + typeid(x).name()); \ + } \ + } +#endif } // namespace serialize namespace std { @@ -156,17 +175,20 @@ namespace std { // Forwarding functions from std to serialize namespace for integers. template inline std::enable_if_t> read(B& buf, T& value) { + DEBUG_CANARY_READ(buf, value); serialize::read(buf, value); } template inline std::enable_if_t> write(B& buf, T value) { + DEBUG_CANARY_WRITE(buf, value); serialize::write(buf, value); } // Optimised specialisation for reading arrays of bytes from a raw buffer. template inline void read(uint8_t const*& it, std::array& value) { + DEBUG_CANARY_READ(it, value); std::copy(it, it + N, value.data()); it += N; } @@ -174,6 +196,7 @@ template inline void read(uint8_t const*& it, std::array& // Optimised specialisation for writing arrays of bytes to a raw buffer. template inline void write(uint8_t*& buf, std::array const& value) { + DEBUG_CANARY_WRITE(buf, value); std::copy(value.begin(), value.end(), buf); buf += N; } @@ -181,6 +204,7 @@ template inline void write(uint8_t*& buf, std::array cons // Optimised specialisation for reading vectors of bytes from a raw buffer. inline void read(uint8_t const*& it, std::vector& value) { + DEBUG_CANARY_READ(it, value); uint32_t size; read(it, size); value.resize(size); @@ -191,6 +215,7 @@ inline void read(uint8_t const*& it, std::vector& value) // Optimised specialisation for writing vectors of bytes to a raw buffer. inline void write(uint8_t*& buf, std::vector const& value) { + DEBUG_CANARY_WRITE(buf, value); write(buf, static_cast(value.size())); std::copy(value.begin(), value.end(), buf); buf += value.size(); @@ -199,6 +224,7 @@ inline void write(uint8_t*& buf, std::vector const& value) // Optimised specialisation for reading vectors of bytes from an input stream. inline void read(std::istream& is, std::vector& value) { + DEBUG_CANARY_READ(is, value); uint32_t size; read(is, size); value.resize(size); @@ -208,6 +234,7 @@ inline void read(std::istream& is, std::vector& value) // Optimised specialisation for writing vectors of bytes to an output stream. inline void write(std::ostream& os, std::vector const& value) { + DEBUG_CANARY_WRITE(os, value); write(os, static_cast(value.size())); os.write((char*)value.data(), (std::streamsize)value.size()); } @@ -215,6 +242,7 @@ inline void write(std::ostream& os, std::vector const& value) // Optimised specialisation for writing arrays of bytes to a vector. template inline void write(std::vector& buf, std::array const& value) { + DEBUG_CANARY_WRITE(buf, value); buf.resize(buf.size() + N); auto ptr = &*buf.end() - N; write(ptr, value); @@ -223,12 +251,14 @@ template inline void write(std::vector& buf, std::array inline void write(std::ostream& os, std::array const& value) { + DEBUG_CANARY_WRITE(os, value); os.write((char*)value.data(), value.size()); } // Generic read of array of types from supported buffer types. template inline void read(B& it, std::array& value) { + DEBUG_CANARY_READ(it, value); for (size_t i = 0; i < N; ++i) { read(it, value[i]); } @@ -237,6 +267,7 @@ template inline void read(B& it, std::array inline void write(B& buf, std::array const& value) { + DEBUG_CANARY_WRITE(buf, value); for (size_t i = 0; i < N; ++i) { write(buf, value[i]); } @@ -245,6 +276,7 @@ template inline void write(B& buf, std::array // Generic read of vector of types from supported buffer types. template inline void read(B& it, std::vector& value) { + DEBUG_CANARY_READ(it, value); uint32_t size; read(it, size); value.resize(size); @@ -265,6 +297,7 @@ template inline void write(B& buf, std::vector const // Read string from supported buffer types. template inline void read(B& it, std::string& value) { + DEBUG_CANARY_READ(it, value); std::vector buf; read(it, buf); value = std::string(buf.begin(), buf.end()); @@ -279,6 +312,7 @@ template inline void write(B& buf, std::string const& value) // Read std::pair. template inline void read(B& it, std::pair& value) { + DEBUG_CANARY_READ(it, value); read(it, value.first); read(it, value.second); } @@ -286,6 +320,7 @@ template inline void read(B& it, std::pair< // Write std::pair. template inline void write(B& buf, std::pair const& value) { + DEBUG_CANARY_WRITE(buf, value); write(buf, value.first); write(buf, value.second); } @@ -293,6 +328,7 @@ template inline void write(B& buf, std::pai // Read std::map template inline void read(B& it, std::map& value) { + DEBUG_CANARY_READ(it, value); value.clear(); uint32_t size; read(it, size); @@ -306,6 +342,7 @@ template inline void read(B& it, std::map inline void write(B& buf, std::map const& value) { + DEBUG_CANARY_WRITE(buf, value); write(buf, static_cast(value.size())); for (auto const& kv : value) { write(buf, kv); @@ -315,6 +352,7 @@ template inline void write(B& buf, std::map // Read std::optional. template inline void read(B& it, std::optional& opt_value) { + DEBUG_CANARY_READ(it, opt_value); bool is_nullopt; read(it, is_nullopt); if (is_nullopt) { @@ -331,6 +369,7 @@ template inline void read(B& it, std::optional& opt_ // value. template inline void write(B& buf, std::optional const& opt_value) { + DEBUG_CANARY_WRITE(buf, opt_value); if (opt_value) { write(buf, false); // is not nullopt write(buf, *opt_value);