From a4fc1ad89aa4ca13d96236e9f32a9c76e5405399 Mon Sep 17 00:00:00 2001 From: Michal Lesiak Date: Thu, 3 Nov 2022 13:13:34 +0100 Subject: [PATCH 1/3] Add overflow check to output_stream. Buffer in output_stream (std_out and std_err) is capped at 2048. Trying to add more data to output_stream will be silently ignored. Add unit tests for overflow. --- libraries/native/native/eosio/crt.hpp | 8 +++- tests/CMakeLists.txt | 1 + tests/unit/CMakeLists.txt | 1 + tests/unit/crt_tests.cpp | 53 +++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/unit/crt_tests.cpp diff --git a/libraries/native/native/eosio/crt.hpp b/libraries/native/native/eosio/crt.hpp index 770a49bba2..a82a49bfac 100644 --- a/libraries/native/native/eosio/crt.hpp +++ b/libraries/native/native/eosio/crt.hpp @@ -8,11 +8,15 @@ namespace eosio { namespace cdt { none }; struct output_stream { - char output[1024*2]; + static constexpr size_t max_size = 1024 * 2; + char output[max_size] = {}; size_t index = 0; std::string to_string()const { return std::string((const char*)output, index); } const char* get()const { return output; } - void push(char c) { output[index++] = c; } + void push(char c) { + if(index < max_size - 1) + output[index++] = c; + } void clear() { index = 0; } }; }} //ns eosio::cdt diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9396d8b6c3..27526c9e56 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,7 @@ endmacro() add_unit_test( asset_tests ) add_unit_test( binary_extension_tests ) +add_unit_test( crt_tests ) add_unit_test( crypto_tests ) add_unit_test( crypto_ext_tests ) add_unit_test( datastream_tests ) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index dd1cad90a9..6dc67b4564 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -10,6 +10,7 @@ endmacro() add_cdt_unit_test(asset_tests) add_cdt_unit_test(binary_extension_tests) +add_cdt_unit_test(crt_tests) add_cdt_unit_test(crypto_tests) add_cdt_unit_test(crypto_ext_tests) add_cdt_unit_test(datastream_tests) diff --git a/tests/unit/crt_tests.cpp b/tests/unit/crt_tests.cpp new file mode 100644 index 0000000000..318afff0b0 --- /dev/null +++ b/tests/unit/crt_tests.cpp @@ -0,0 +1,53 @@ +/** + * @file + * @copyright defined in eosio.cdt/LICENSE.txt + */ +#include +#include + +#include +#include + +#include + +using eosio::cdt::output_stream; + +EOSIO_TEST_BEGIN(output_stream_push) + std_err.clear(); + const char* msg = "abc"; + _prints(msg, eosio::cdt::output_stream_kind::std_err); + CHECK_EQUAL(std_err.to_string(), "abc"); + + std_err.clear(); + const char* msg2 = ""; + _prints(msg2, eosio::cdt::output_stream_kind::std_err); + CHECK_EQUAL(std_err.to_string(), ""); + + std_err.clear(); +EOSIO_TEST_END + +EOSIO_TEST_BEGIN(output_stream_push_overflow) + std_err.clear(); + const auto max_size = std_err.max_size; + + char msg[max_size+1] = {}; + for (auto i = 0; i< max_size; ++i) + msg[i] = 'x'; + + _prints(msg, eosio::cdt::output_stream_kind::std_err); + CHECK_EQUAL(std_err.to_string().size(), max_size - 1); + + std_err.clear(); +EOSIO_TEST_END + +int main(int argc, char* argv[]) { + bool verbose = false; + if( argc >= 2 && std::strcmp( argv[1], "-v" ) == 0 ) { + verbose = true; + } + silence_output(!verbose); + + EOSIO_TEST(output_stream_push) + EOSIO_TEST(output_stream_push_overflow) + return has_failed(); +} From 463575dfc8de15cd2b95aebc164e51248ee8c48f Mon Sep 17 00:00:00 2001 From: Michal Lesiak Date: Fri, 4 Nov 2022 11:47:03 +0100 Subject: [PATCH 2/3] Change char array to string in output_stream to add possibility to extend buffer. --- libraries/native/native/eosio/crt.hpp | 24 +++++++++++++----------- libraries/native/native/eosio/tester.hpp | 4 ++-- tests/unit/crt_tests.cpp | 14 ++++++++------ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/libraries/native/native/eosio/crt.hpp b/libraries/native/native/eosio/crt.hpp index a82a49bfac..0228999ec3 100644 --- a/libraries/native/native/eosio/crt.hpp +++ b/libraries/native/native/eosio/crt.hpp @@ -1,23 +1,25 @@ #pragma once #include +#include + namespace eosio { namespace cdt { enum output_stream_kind { std_out, std_err, none }; - struct output_stream { - static constexpr size_t max_size = 1024 * 2; - char output[max_size] = {}; - size_t index = 0; - std::string to_string()const { return std::string((const char*)output, index); } - const char* get()const { return output; } - void push(char c) { - if(index < max_size - 1) - output[index++] = c; - } - void clear() { index = 0; } + class output_stream { + static constexpr size_t initial_size = 1024 * 4; + std::string output; + + public: + output_stream() { output.reserve(initial_size); } + std::string to_string() const { return output; } + const char* get() const { return output.c_str(); } + size_t index() const { return output.size(); } + void push(char c) { output.push_back(c); } + void clear() { output.clear(); } }; }} //ns eosio::cdt diff --git a/libraries/native/native/eosio/tester.hpp b/libraries/native/native/eosio/tester.hpp index e824ef4a83..0a9511ec85 100644 --- a/libraries/native/native/eosio/tester.hpp +++ b/libraries/native/native/eosio/tester.hpp @@ -51,7 +51,7 @@ template inline bool expect_assert(bool check, const std::string& li, const char (&expected)[N], F&& func, Args... args) { return expect_assert(check, li, [&](const std::string& s) { - return std_err.index == N-1 && + return std_err.index() == N-1 && memcmp(expected, s.c_str(), N-1) == 0; }, func, args...); } @@ -75,7 +75,7 @@ template inline bool expect_print(bool check, const std::string& li, const char (&expected)[N], F&& func, Args... args) { return expect_print(check, li, [&](const std::string& s) { - return std_out.index == N-1 && + return std_out.index() == N-1 && memcmp(expected, s.c_str(), N-1) == 0; }, func, args...); } diff --git a/tests/unit/crt_tests.cpp b/tests/unit/crt_tests.cpp index 318afff0b0..3ae74029c1 100644 --- a/tests/unit/crt_tests.cpp +++ b/tests/unit/crt_tests.cpp @@ -17,25 +17,27 @@ EOSIO_TEST_BEGIN(output_stream_push) const char* msg = "abc"; _prints(msg, eosio::cdt::output_stream_kind::std_err); CHECK_EQUAL(std_err.to_string(), "abc"); + CHECK_EQUAL(std_err.index(), 3); std_err.clear(); const char* msg2 = ""; _prints(msg2, eosio::cdt::output_stream_kind::std_err); CHECK_EQUAL(std_err.to_string(), ""); + CHECK_EQUAL(std_err.index(), 0); std_err.clear(); EOSIO_TEST_END EOSIO_TEST_BEGIN(output_stream_push_overflow) std_err.clear(); - const auto max_size = std_err.max_size; + const auto initial_capacity = std_err.to_string().capacity(); + CHECK_EQUAL(std_err.index(), 0); - char msg[max_size+1] = {}; - for (auto i = 0; i< max_size; ++i) - msg[i] = 'x'; + std::string large_msg('x', initial_capacity + 1); - _prints(msg, eosio::cdt::output_stream_kind::std_err); - CHECK_EQUAL(std_err.to_string().size(), max_size - 1); + _prints(large_msg.c_str(), eosio::cdt::output_stream_kind::std_err); + CHECK_EQUAL(std_err.to_string().capacity() > initial_capacity, true); + CHECK_EQUAL(std_err.index(), large_msg.size()); std_err.clear(); EOSIO_TEST_END From bb3f98593c2dc9f10536a89fd02b29adeedebac1 Mon Sep 17 00:00:00 2001 From: Michal Lesiak Date: Wed, 9 Nov 2022 11:45:11 +0100 Subject: [PATCH 3/3] Adressed code review. Added unit test for get and push. --- libraries/native/native/eosio/crt.hpp | 2 +- tests/unit/crt_tests.cpp | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/libraries/native/native/eosio/crt.hpp b/libraries/native/native/eosio/crt.hpp index 0228999ec3..5d81a60015 100644 --- a/libraries/native/native/eosio/crt.hpp +++ b/libraries/native/native/eosio/crt.hpp @@ -15,7 +15,7 @@ namespace eosio { namespace cdt { public: output_stream() { output.reserve(initial_size); } - std::string to_string() const { return output; } + const std::string& to_string() const { return output; } const char* get() const { return output.c_str(); } size_t index() const { return output.size(); } void push(char c) { output.push_back(c); } diff --git a/tests/unit/crt_tests.cpp b/tests/unit/crt_tests.cpp index 3ae74029c1..9aa5060a58 100644 --- a/tests/unit/crt_tests.cpp +++ b/tests/unit/crt_tests.cpp @@ -2,14 +2,14 @@ * @file * @copyright defined in eosio.cdt/LICENSE.txt */ + #include +#include #include #include #include -#include - using eosio::cdt::output_stream; EOSIO_TEST_BEGIN(output_stream_push) @@ -42,6 +42,19 @@ EOSIO_TEST_BEGIN(output_stream_push_overflow) std_err.clear(); EOSIO_TEST_END +EOSIO_TEST_BEGIN(output_stream_get_and_push) + std_err.clear(); + std::string example_msg("abcdef"); + + _prints(example_msg.c_str(), eosio::cdt::output_stream_kind::std_err); + CHECK_EQUAL(strcmp(std_err.get(), "abcdef") , 0); + + std_err.push('g'); + CHECK_EQUAL(strcmp(std_err.get(), "abcdefg") , 0); + + std_err.clear(); +EOSIO_TEST_END + int main(int argc, char* argv[]) { bool verbose = false; if( argc >= 2 && std::strcmp( argv[1], "-v" ) == 0 ) { @@ -51,5 +64,6 @@ int main(int argc, char* argv[]) { EOSIO_TEST(output_stream_push) EOSIO_TEST(output_stream_push_overflow) + EOSIO_TEST(output_stream_get_and_push) return has_failed(); }