From 0391665471b4dffb7af951853a777565a7ae9a74 Mon Sep 17 00:00:00 2001 From: Vikram Rajkumar Date: Mon, 13 Jul 2015 14:28:44 -0400 Subject: [PATCH] Fix and cleanup safe spec; cryptonomex/graphene#10 --- include/fc/exception/exception.hpp | 66 ++++----- include/fc/safe.hpp | 208 +++++++++++++++++------------ src/exception.cpp | 43 +++--- 3 files changed, 179 insertions(+), 138 deletions(-) diff --git a/include/fc/exception/exception.hpp b/include/fc/exception/exception.hpp index 1b8d8ed..73e4bf5 100644 --- a/include/fc/exception/exception.hpp +++ b/include/fc/exception/exception.hpp @@ -1,7 +1,7 @@ #pragma once /** * @file exception.hpp - * @brief Defines exception's used by fc + * @brief Defines exception's used by fc */ #include #include @@ -13,10 +13,10 @@ namespace fc { namespace detail { class exception_impl; } - enum exception_code + enum exception_code { /** for exceptions we threw that don't have an assigned code */ - unspecified_exception_code = 0, + unspecified_exception_code = 0, unhandled_exception_code = 1, ///< for unhandled 3rd party exceptions timeout_exception_code = 2, ///< timeout exceptions file_not_found_exception_code = 3, @@ -35,7 +35,8 @@ namespace fc udt_error_code = 17, aes_error_code = 18, overflow_code = 19, - underflow_code = 20 + underflow_code = 20, + divide_by_zero_code = 21 }; /** @@ -46,21 +47,21 @@ namespace fc * new log_message is added to the exception. * * exception's are designed to be serialized to a variant and - * deserialized from an variant. + * deserialized from an variant. * * @see FC_THROW_EXCEPTION * @see FC_RETHROW_EXCEPTION * @see FC_RETHROW_EXCEPTIONS */ - class exception + class exception { public: - enum code_enum + enum code_enum { code_value = unspecified_exception_code }; - exception( int64_t code = unspecified_exception_code, + exception( int64_t code = unspecified_exception_code, const std::string& name_value = "exception", const std::string& what_value = "unspecified"); exception( log_message&&, int64_t code = unspecified_exception_code, @@ -83,7 +84,7 @@ namespace fc */ const log_messages& get_log()const; void append_log( log_message m ); - + /** * Generates a detailed string including file, line, method, * and other information that is generally only useful for @@ -97,7 +98,7 @@ namespace fc std::string to_string( log_level ll = log_level::info )const; /** - * Throw this exception as its most derived type. + * Throw this exception as its most derived type. * * @note does not return. */ @@ -106,7 +107,7 @@ namespace fc /** * This is equivalent to: * @code - * try { throwAsDynamic_exception(); } + * try { throwAsDynamic_exception(); } * catch( ... ) { return std::current_exception(); } * @endcode */ @@ -132,21 +133,21 @@ namespace fc * @brief re-thrown whenever an unhandled exception is caught. * @ingroup serializable * Any exceptions thrown by 3rd party libraries that are not - * caught get wrapped in an unhandled_exception exception. + * caught get wrapped in an unhandled_exception exception. * - * The original exception is captured as a std::exception_ptr + * The original exception is captured as a std::exception_ptr * which may be rethrown. The std::exception_ptr does not - * propgate across process boundaries. + * propgate across process boundaries. */ - class unhandled_exception : public exception - { - public: - enum code_enum { - code_value = unhandled_exception_code, - }; - unhandled_exception( log_message&& m, std::exception_ptr e = std::current_exception() ); - unhandled_exception( log_messages ); - unhandled_exception( const exception& ); + class unhandled_exception : public exception + { + public: + enum code_enum { + code_value = unhandled_exception_code, + }; + unhandled_exception( log_message&& m, std::exception_ptr e = std::current_exception() ); + unhandled_exception( log_messages ); + unhandled_exception( const exception& ); std::exception_ptr get_inner_exception()const; @@ -160,10 +161,10 @@ namespace fc fc::exception_ptr copy_exception( T&& e ) { #if defined(_MSC_VER) && (_MSC_VER < 1700) - return std::make_shared( log_message(), + return std::make_shared( log_message(), std::copy_exception(fc::forward(e)) ); #else - return std::make_shared( log_message(), + return std::make_shared( log_message(), std::make_exception_ptr(fc::forward(e)) ); #endif } @@ -177,7 +178,7 @@ namespace fc virtual NO_RETURN void rethrow( const exception& e )const = 0; }; - template + template struct exception_builder : public base_exception_builder { virtual NO_RETURN void rethrow( const exception& e )const override @@ -195,7 +196,7 @@ namespace fc (void)itr; // in release builds this hides warnings _registered_exceptions[T::code_value] = &builder; } - + void NO_RETURN rethrow( const exception& e )const; static exception_factory& instance() @@ -248,7 +249,7 @@ namespace fc else fc::exception::dynamic_rethrow_exception(); \ } \ }; - + #define FC_DECLARE_EXCEPTION( TYPE, CODE, WHAT ) \ FC_DECLARE_DERIVED_EXCEPTION( TYPE, fc::exception, CODE, WHAT ) @@ -267,8 +268,8 @@ namespace fc FC_DECLARE_EXCEPTION( out_of_range_exception, out_of_range_exception_code, "Out of Range" ); /** @brief if an operation is unsupported or not valid this may be thrown */ - FC_DECLARE_EXCEPTION( invalid_operation_exception, - invalid_operation_exception_code, + FC_DECLARE_EXCEPTION( invalid_operation_exception, + invalid_operation_exception_code, "Invalid Operation" ); /** @brief if an host name can not be resolved this may be thrown */ FC_DECLARE_EXCEPTION( unknown_host_exception, @@ -289,6 +290,7 @@ namespace fc FC_DECLARE_EXCEPTION( aes_exception, aes_error_code, "AES error" ); FC_DECLARE_EXCEPTION( overflow_exception, overflow_code, "Integer Overflow" ); FC_DECLARE_EXCEPTION( underflow_exception, underflow_code, "Integer Underflow" ); + FC_DECLARE_EXCEPTION( divide_by_zero_exception, divide_by_zero_code, "Integer Divide By Zero" ); std::string except_str(); @@ -333,7 +335,7 @@ namespace fc throw EXCEPTION_TYPE( FC_LOG_MESSAGE( error, "", FC_FORMAT_ARG_PARAMS(__VA_ARGS__) ) ); \ FC_MULTILINE_MACRO_END -//#define FC_THROW( FORMAT, ... ) +//#define FC_THROW( FORMAT, ... ) // FC_INDIRECT_EXPAND workas around a bug in Visual C++ variadic macro processing that prevents it // from separating __VA_ARGS__ into separate tokens #define FC_INDIRECT_EXPAND(MACRO, ARGS) MACRO ARGS @@ -343,7 +345,7 @@ namespace fc FC_MULTILINE_MACRO_END #define FC_EXCEPTION( EXCEPTION_TYPE, FORMAT, ... ) \ - EXCEPTION_TYPE( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ) + EXCEPTION_TYPE( FC_LOG_MESSAGE( error, FORMAT, __VA_ARGS__ ) ) /** * @def FC_THROW_EXCEPTION( EXCEPTION, FORMAT, ... ) * @param EXCEPTION a class in the Phoenix::Athena::API namespace that inherits diff --git a/include/fc/safe.hpp b/include/fc/safe.hpp index 7d876c0..9afcf16 100644 --- a/include/fc/safe.hpp +++ b/include/fc/safe.hpp @@ -13,126 +13,164 @@ namespace fc { * * It can only be used on built-in types. In particular, * safe is buggy and should not be used. + * + * Implemented using spec from: + * https://www.securecoding.cert.org/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow */ template struct safe { + T value = 0; + template safe( O o ):value(o){} safe(){} safe( const safe& o ):value(o.value){} - static safe max() - { return std::numeric_limits::max(); } static safe min() - { return std::numeric_limits::min(); } - - safe& operator += ( const safe& b ) { - if( b.value > 0 && value > (std::numeric_limits::max() - b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (*this)(b) ); - if( b.value < 0 && value < (std::numeric_limits::min() - b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (*this)(b) ); - value += b.value; - return *this; + return std::numeric_limits::min(); } + static safe max() + { + return std::numeric_limits::max(); + } + friend safe operator + ( const safe& a, const safe& b ) { - if( b.value > 0 && a.value > std::numeric_limits::max() - b.value ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); - if( b.value < 0 && a.value < std::numeric_limits::min() - b.value ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); - return safe(a.value+b.value); + if( b.value > 0 && a.value > (std::numeric_limits::max() - b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + if( b.value < 0 && a.value < (std::numeric_limits::min() - b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + return safe( a.value + b.value ); } - safe& operator *= ( safe v ) { value *= v.value; return *this; } - safe& operator /= ( safe v ) { FC_ASSERT(v.value != 0); value /= v.value; return *this; } - safe& operator -= ( const safe& b ) { return *this += safe(-b.value); } - safe operator -()const - { - if( value == std::numeric_limits::min() ) - FC_CAPTURE_AND_THROW( overflow_exception, (value) ); - return safe(-value); - } - - safe operator++(int) { safe bak = *this; *this += 1; return bak; } - safe& operator++() { return *this += 1; } - safe operator--(int) { safe bak = *this; *this -= 1; return bak; } - safe& operator--() { return *this -= 1; } - friend safe operator - ( const safe& a, const safe& b ) { - safe tmp(a); tmp -= b; return tmp; + if( b.value > 0 && a.value < (std::numeric_limits::min() + b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + if( b.value < 0 && a.value > (std::numeric_limits::max() + b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value - b.value ); + } + + friend safe operator * ( const safe& a, const safe& b ) + { + if( a.value > 0 ) + { + if( b.value > 0 ) + { + if( a.value > (std::numeric_limits::max() / b.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + } + else + { + if( b.value < (std::numeric_limits::min() / a.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + } + } + else + { + if( b.value > 0 ) + { + if( a.value < (std::numeric_limits::min() / b.value) ) FC_CAPTURE_AND_THROW( underflow_exception, (a)(b) ); + } + else + { + if( a.value != 0 && b.value < (std::numeric_limits::max() / a.value) ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + } + } + + return safe( a.value * b.value ); + } + + friend safe operator / ( const safe& a, const safe& b ) + { + if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) ); + if( a.value == std::numeric_limits::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value / b.value ); + } + friend safe operator % ( const safe& a, const safe& b ) + { + if( b.value == 0 ) FC_CAPTURE_AND_THROW( divide_by_zero_exception, (a)(b) ); + if( a.value == std::numeric_limits::min() && b.value == -1 ) FC_CAPTURE_AND_THROW( overflow_exception, (a)(b) ); + return safe( a.value % b.value ); + } + + safe operator - ()const + { + if( value == std::numeric_limits::min() ) FC_CAPTURE_AND_THROW( overflow_exception, (*this) ); + return safe( -value ); + } + + safe& operator += ( const safe& b ) + { + value = (*this + b).value; + return *this; + } + safe& operator -= ( const safe& b ) + { + value = (*this - b).value; + return *this; + } + safe& operator *= ( const safe& b ) + { + value = (*this * b).value; + return *this; + } + safe& operator /= ( const safe& b ) + { + value = (*this / b).value; + return *this; + } + safe& operator %= ( const safe& b ) + { + value = (*this % b).value; + return *this; + } + + safe& operator++() + { + *this += 1; + return *this; + } + safe operator++( int ) + { + safe bak = *this; + *this += 1; + return bak; + } + + safe& operator--() + { + *this -= 1; + return *this; + } + safe operator--( int ) + { + safe bak = *this; + *this -= 1; + return bak; } friend bool operator == ( const safe& a, const safe& b ) { - return a.value == b.value; - } - friend bool operator == ( const safe& a, const T& b ) - { - return a.value == b; - } - friend bool operator == ( const T& a, const safe& b ) - { - return a == b.value; - } - friend bool operator != ( const safe& a, const safe& b ) - { - return a.value != b.value; - } - friend bool operator != ( const safe& a, const T& b ) - { - return a.value != b; - } - friend bool operator != ( const T& a, const safe& b ) - { - return a != b.value; + return a.value == b.value; } friend bool operator < ( const safe& a, const safe& b ) { - return a.value < b.value; - } - friend bool operator < ( const safe& a, const T& b ) - { - return a.value < b; - } - friend bool operator < ( const T& a, const safe& b ) - { - return a < b.value; + return a.value < b.value; } friend bool operator > ( const safe& a, const safe& b ) { - return a.value > b.value; + return a.value > b.value; } - friend bool operator > ( const safe& a, const T& b ) + + friend bool operator != ( const safe& a, const safe& b ) { - return a.value > b; - } - friend bool operator > ( const T& a, const safe& b ) - { - return a > b.value; - } - friend bool operator >= ( const safe& a, const safe& b ) - { - return a.value >= b.value; - } - friend bool operator >= ( const safe& a, const T& b ) - { - return a.value >= b; - } - friend bool operator >= ( const T& a, const safe& b ) - { - return a >= b.value; + return !(a == b); } friend bool operator <= ( const safe& a, const safe& b ) { - return a.value <= b.value; + return !(a > b ); } - friend bool operator <= ( const safe& a, const T& b ) + friend bool operator >= ( const safe& a, const safe& b ) { - return a.value <= b; + return !(a < b); } - friend bool operator <= ( const T& a, const safe& b ) - { - return a <= b.value; - } - T value = 0; }; } diff --git a/src/exception.cpp b/src/exception.cpp index 79b37ae..8887922 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -25,6 +25,7 @@ namespace fc (aes_exception) (overflow_exception) (underflow_exception) + (divide_by_zero_exception) ) namespace detail @@ -39,7 +40,7 @@ namespace fc }; } exception::exception( log_messages&& msgs, int64_t code, - const std::string& name_value, + const std::string& name_value, const std::string& what_value ) :my( new detail::exception_impl() ) { @@ -49,18 +50,18 @@ namespace fc my->_elog = fc::move(msgs); } - unhandled_exception::unhandled_exception( log_message&& m, std::exception_ptr e ) - :exception( fc::move(m) ) - { + unhandled_exception::unhandled_exception( log_message&& m, std::exception_ptr e ) + :exception( fc::move(m) ) + { _inner = e; - } + } unhandled_exception::unhandled_exception( const exception& r ) :exception(r) { } - unhandled_exception::unhandled_exception( log_messages m ) - :exception() - { my->_elog = fc::move(m); } + unhandled_exception::unhandled_exception( log_messages m ) + :exception() + { my->_elog = fc::move(m); } std::exception_ptr unhandled_exception::get_inner_exception()const { return _inner; } @@ -77,9 +78,9 @@ namespace fc return e; } - exception::exception( int64_t code, + exception::exception( int64_t code, const std::string& name_value, - const std::string& what_value ) + const std::string& what_value ) :my( new detail::exception_impl() ) { my->_code = code; @@ -87,8 +88,8 @@ namespace fc my->_name = name_value; } - exception::exception( log_message&& msg, - int64_t code, + exception::exception( log_message&& msg, + int64_t code, const std::string& name_value, const std::string& what_value ) :my( new detail::exception_impl() ) @@ -116,7 +117,7 @@ namespace fc ( "name", e.name() ) ( "message", e.what() ) ( "stack", e.get_log() ); - + } void from_variant( const variant& v, exception& ll ) { @@ -136,7 +137,7 @@ namespace fc { my->_elog.emplace_back( fc::move(m) ); } - + /** * Generates a detailed string including file, line, method, * and other information that is generally only useful for @@ -189,26 +190,26 @@ namespace fc exception_factory::instance().rethrow( *this ); } - exception_ptr exception::dynamic_copy_exception()const + exception_ptr exception::dynamic_copy_exception()const { return std::make_shared(*this); } - + fc::string except_str() { - return boost::current_exception_diagnostic_information(); + return boost::current_exception_diagnostic_information(); } void throw_bad_enum_cast( int64_t i, const char* e ) { - FC_THROW_EXCEPTION( bad_cast_exception, - "invalid index '${key}' in enum '${enum}'", + FC_THROW_EXCEPTION( bad_cast_exception, + "invalid index '${key}' in enum '${enum}'", ("key",i)("enum",e) ); } void throw_bad_enum_cast( const char* k, const char* e ) { - FC_THROW_EXCEPTION( bad_cast_exception, - "invalid name '${key}' in enum '${enum}'", + FC_THROW_EXCEPTION( bad_cast_exception, + "invalid name '${key}' in enum '${enum}'", ("key",k)("enum",e) ); }