// Copyright (c) 2006-2013, Andrey N. Sabelnikov, www.sabelnikov.net // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of the Andrey N. Sabelnikov nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER BE LIABLE FOR ANY // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // #pragma once #include "misc_language.h" #include "portable_storage_base.h" #include "portable_storage_bin_utils.h" #ifdef EPEE_PORTABLE_STORAGE_RECURSION_LIMIT #define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL EPEE_PORTABLE_STORAGE_RECURSION_LIMIT #else #define EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL 100 #endif #define EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL 65536 #define EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL 65536 namespace epee { namespace serialization { template struct ps_min_bytes { static constexpr const size_t strict = 4096; // actual low bound static constexpr const size_t rough = 4096; // when we want to be stricter for DoS prevention }; template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 4, rough = 4; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 4, rough = 4; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 2; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 2; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 8, rough = 8; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 1; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 2, rough = 16; }; template<> struct ps_min_bytes
{ static constexpr const size_t strict = 1, rough = 256; }; template<> struct ps_min_bytes { static constexpr const size_t strict = 1, rough = 128; }; struct throwable_buffer_reader { throwable_buffer_reader(const void* ptr, size_t sz); void read(void* target, size_t count); void read_sec_name(std::string& sce_name); template void read(t_pod_type& pod_val); template t_type read(); template storage_entry read_ae(); storage_entry load_storage_array_entry(uint8_t type); size_t read_varint(); template storage_entry read_se(); storage_entry load_storage_entry(); void read(section& sec); void read(std::string& str); void read(array_entry &ae); template size_t min_bytes() const; private: struct recursuion_limitation_guard { size_t& m_counter_ref; recursuion_limitation_guard(size_t& counter):m_counter_ref(counter) { ++m_counter_ref; CHECK_AND_ASSERT_THROW_MES(m_counter_ref < EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL, "Wrong blob data in portable storage: recursion limitation (" << EPEE_PORTABLE_STORAGE_RECURSION_LIMIT_INTERNAL << ") exceeded"); } ~recursuion_limitation_guard() noexcept(false) { CHECK_AND_ASSERT_THROW_MES(m_counter_ref != 0, "Internal error: m_counter_ref == 0 while ~recursuion_limitation_guard()"); --m_counter_ref; } }; #define RECURSION_LIMITATION() recursuion_limitation_guard rl(m_recursion_count) const uint8_t* m_ptr; size_t m_count; size_t m_recursion_count; size_t m_objects; size_t m_array_elements; }; inline throwable_buffer_reader::throwable_buffer_reader(const void* ptr, size_t sz) { if(!ptr) throw std::runtime_error("throwable_buffer_reader: ptr==nullptr"); if(!sz) throw std::runtime_error("throwable_buffer_reader: sz==0"); m_ptr = (uint8_t*)ptr; m_count = sz; m_recursion_count = 0; m_objects = 0; m_array_elements = 0; } inline void throwable_buffer_reader::read(void* target, size_t count) { RECURSION_LIMITATION(); CHECK_AND_ASSERT_THROW_MES(m_count >= count, " attempt to read " << count << " bytes from buffer with " << m_count << " bytes remained"); memcpy(target, m_ptr, count); m_ptr += count; m_count -= count; } inline void throwable_buffer_reader::read_sec_name(std::string& sce_name) { RECURSION_LIMITATION(); uint8_t name_len = 0; read(name_len); sce_name.resize(name_len); read((void*)sce_name.data(), name_len); } template void throwable_buffer_reader::read(t_pod_type& pod_val) { RECURSION_LIMITATION(); static_assert(std::is_pod::value, "POD type expected"); read(&pod_val, sizeof(pod_val)); pod_val = CONVERT_POD(pod_val); } template t_type throwable_buffer_reader::read() { RECURSION_LIMITATION(); t_type v; read(v); return v; } template storage_entry throwable_buffer_reader::read_ae() { RECURSION_LIMITATION(); //for pod types array_entry_t sa; size_t size = read_varint(); CHECK_AND_ASSERT_THROW_MES(size < EPEE_PORTABLE_STORAGE_ARRAY_ELEMENT_LIMIT_INTERNAL - m_array_elements, "Too many array elements"); m_array_elements += size; CHECK_AND_ASSERT_THROW_MES(size <= m_count / ps_min_bytes::strict, "Size sanity check failed"); sa.reserve(size); //TODO: add some optimization here later while(size--) sa.m_array.push_back(read()); return storage_entry(array_entry(std::move(sa))); } inline storage_entry throwable_buffer_reader::load_storage_array_entry(uint8_t type) { RECURSION_LIMITATION(); type &= ~SERIALIZE_FLAG_ARRAY; switch(type) { case SERIALIZE_TYPE_INT64: return read_ae(); case SERIALIZE_TYPE_INT32: return read_ae(); case SERIALIZE_TYPE_INT16: return read_ae(); case SERIALIZE_TYPE_INT8: return read_ae(); case SERIALIZE_TYPE_UINT64: return read_ae(); case SERIALIZE_TYPE_UINT32: return read_ae(); case SERIALIZE_TYPE_UINT16: return read_ae(); case SERIALIZE_TYPE_UINT8: return read_ae(); case SERIALIZE_TYPE_DUOBLE: return read_ae(); case SERIALIZE_TYPE_BOOL: return read_ae(); case SERIALIZE_TYPE_STRING: return read_ae(); case SERIALIZE_TYPE_OBJECT: return read_ae
(); case SERIALIZE_TYPE_ARRAY: return read_ae(); default: CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << type); } } inline size_t throwable_buffer_reader::read_varint() { RECURSION_LIMITATION(); CHECK_AND_ASSERT_THROW_MES(m_count >= 1, "empty buff, expected place for varint"); size_t v = 0; uint8_t size_mask = (*(uint8_t*)m_ptr) &PORTABLE_RAW_SIZE_MARK_MASK; switch (size_mask) { case PORTABLE_RAW_SIZE_MARK_BYTE: v = read();break; case PORTABLE_RAW_SIZE_MARK_WORD: v = read();break; case PORTABLE_RAW_SIZE_MARK_DWORD: v = read();break; case PORTABLE_RAW_SIZE_MARK_INT64: v = read();break; default: CHECK_AND_ASSERT_THROW_MES(false, "unknown varint size_mask = " << size_mask); } v >>= 2; return v; } template storage_entry throwable_buffer_reader::read_se() { RECURSION_LIMITATION(); t_type v; read(v); return storage_entry(v); } template<> inline storage_entry throwable_buffer_reader::read_se() { RECURSION_LIMITATION(); return storage_entry(read()); } template<> inline storage_entry throwable_buffer_reader::read_se
() { RECURSION_LIMITATION(); section s;//use extra variable due to vs bug, line "storage_entry se(section()); " can't be compiled in visual studio storage_entry se(std::move(s)); section& section_entry = boost::get
(se); read(section_entry); return se; } template<> inline storage_entry throwable_buffer_reader::read_se() { RECURSION_LIMITATION(); uint8_t ent_type = 0; read(ent_type); CHECK_AND_ASSERT_THROW_MES(ent_type&SERIALIZE_FLAG_ARRAY, "wrong type sequenses"); return load_storage_array_entry(ent_type); } inline storage_entry throwable_buffer_reader::load_storage_entry() { RECURSION_LIMITATION(); uint8_t ent_type = 0; read(ent_type); if(ent_type&SERIALIZE_FLAG_ARRAY) return load_storage_array_entry(ent_type); switch(ent_type) { case SERIALIZE_TYPE_INT64: return read_se(); case SERIALIZE_TYPE_INT32: return read_se(); case SERIALIZE_TYPE_INT16: return read_se(); case SERIALIZE_TYPE_INT8: return read_se(); case SERIALIZE_TYPE_UINT64: return read_se(); case SERIALIZE_TYPE_UINT32: return read_se(); case SERIALIZE_TYPE_UINT16: return read_se(); case SERIALIZE_TYPE_UINT8: return read_se(); case SERIALIZE_TYPE_DUOBLE: return read_se(); case SERIALIZE_TYPE_BOOL: return read_se(); case SERIALIZE_TYPE_STRING: return read_se(); case SERIALIZE_TYPE_OBJECT: return read_se
(); case SERIALIZE_TYPE_ARRAY: return read_se(); default: CHECK_AND_ASSERT_THROW_MES(false, "unknown entry_type code = " << ent_type); } } inline void throwable_buffer_reader::read(section& sec) { RECURSION_LIMITATION(); sec.m_entries.clear(); size_t count = read_varint(); CHECK_AND_ASSERT_THROW_MES(count < EPEE_PORTABLE_STORAGE_OBJECT_LIMIT_INTERNAL - m_objects, "Too many objects"); m_objects += count; while(count--) { //read section name string std::string sec_name; read_sec_name(sec_name); sec.m_entries.emplace(std::move(sec_name), load_storage_entry()); } } inline void throwable_buffer_reader::read(std::string& str) { RECURSION_LIMITATION(); size_t len = read_varint(); CHECK_AND_ASSERT_THROW_MES(len < MAX_STRING_LEN_POSSIBLE, "to big string len value in storage: " << len); CHECK_AND_ASSERT_THROW_MES(m_count >= len, "string len count value " << len << " goes out of remain storage len " << m_count); //do this manually to avoid double memory write in huge strings (first time at resize, second at read) str.assign((const char*)m_ptr, len); m_ptr+=len; m_count -= len; } inline void throwable_buffer_reader::read(array_entry &ae) { RECURSION_LIMITATION(); CHECK_AND_ASSERT_THROW_MES(false, "Reading array entry is not supported"); } } }