Merge remote-tracking branch 'origin/feature/secure_strings' into development

This commit is contained in:
John Wellbelove 2019-05-12 11:06:02 +01:00
commit 50a8b72697
9 changed files with 573 additions and 17 deletions

View File

@ -50,6 +50,7 @@ SOFTWARE.
#include "error_handler.h"
#include "integral_limits.h"
#include "exception.h"
#include "memory.h"
#undef ETL_FILE
#define ETL_FILE "27"
@ -229,15 +230,32 @@ namespace etl
is_truncated = false;
}
//*************************************************************************
/// Sets the 'secure' flag to the requested state.
//*************************************************************************
void set_secure()
{
clear_afer_use = true;
}
//*************************************************************************
/// Gets the 'secure' state flag.
//*************************************************************************
bool is_secure() const
{
return clear_afer_use;
}
protected:
//*************************************************************************
/// Constructor.
//*************************************************************************
string_base(size_t max_size_)
: is_truncated(false),
current_size(0),
CAPACITY(max_size_)
: is_truncated(false)
, clear_afer_use(false)
, current_size(0)
, CAPACITY(max_size_)
{
}
@ -248,9 +266,10 @@ namespace etl
{
}
bool is_truncated; ///< Set to true if the operation truncated the string.
size_type current_size; ///< The current number of elements in the string.
const size_type CAPACITY; ///< The maximum number of elements in the string.
bool is_truncated; ///< Set to true if the operation truncated the string.
bool clear_afer_use; ///< Set to true if the string must be cleared after use.
size_type current_size; ///< The current number of elements in the string.
const size_type CAPACITY; ///< The maximum number of elements in the string.
};
//***************************************************************************
@ -531,6 +550,8 @@ namespace etl
{
is_truncated = true;
}
cleanup();
}
//*********************************************************************
@ -1059,6 +1080,7 @@ namespace etl
current_size -= n_delete;
p_buffer[current_size] = 0;
cleanup();
return first;
}
@ -1383,6 +1405,11 @@ namespace etl
// Insert the new stuff.
insert(position, str, subposition, sublength);
if (str.truncated())
{
is_truncated = true;
}
return *this;
}
@ -1983,7 +2010,8 @@ namespace etl
void initialise()
{
current_size = 0;
p_buffer[0] = 0;
cleanup();
p_buffer[0] = 0;
is_truncated = false;
}
@ -2037,9 +2065,25 @@ namespace etl
}
}
// Disable copy construction.
//*************************************************************************
/// Clear the unused trailing portion of the string.
//*************************************************************************
void cleanup()
{
if (is_secure())
{
etl::memory_clear_range(&p_buffer[current_size], &p_buffer[CAPACITY]);
}
}
//*************************************************************************
/// Disable copy construction.
//*************************************************************************
ibasic_string(const ibasic_string&);
//*************************************************************************
/// Pointer to the string's buffer.
//*************************************************************************
T* p_buffer;
//*************************************************************************
@ -2047,15 +2091,17 @@ namespace etl
//*************************************************************************
#if defined(ETL_POLYMORPHIC_STRINGS) || defined(ETL_POLYMORPHIC_CONTAINERS)
public:
virtual ~ibasic_string()
{
}
virtual
#else
protected:
#endif
~ibasic_string()
{
if (is_secure())
{
initialise();
}
}
#endif
};
//***************************************************************************

View File

@ -1108,7 +1108,6 @@ namespace etl
//*****************************************************************************
/// A low level function that clears an object's memory to zero.
///\param p Pointer to the memory.
///\param n Size of the memory.
///\ingroup memory
@ -1128,11 +1127,39 @@ namespace etl
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_clear(T &object)
void memory_clear(volatile T &object)
{
memory_clear(reinterpret_cast<volatile char*>(&object), sizeof(T));
}
//*****************************************************************************
/// A low level function that clears a range to zero.
///\tparam T The type.
///\param begin The first object in the range.
///\param n The number of objects.
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_clear_range(volatile T* begin, size_t n)
{
memory_clear(reinterpret_cast<volatile char*>(begin), n * sizeof(T));
}
//*****************************************************************************
/// A low level function that clears a range to zero.
///\tparam T The type.
///\param begin The first object in the range.
///\param end One past the last object in the range.
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_clear_range(volatile T* begin, volatile T* end)
{
const size_t n = static_cast<size_t>(std::distance(begin, end));
memory_clear_range(begin, n);
}
//*****************************************************************************
/// A low level function that clears an object's memory to zero.
///\param p Pointer to the memory.
@ -1156,10 +1183,56 @@ namespace etl
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_set(T &object, char value)
void memory_set(volatile T &object, const char value)
{
memory_set(reinterpret_cast<volatile char*>(&object), sizeof(T), value);
}
//*****************************************************************************
/// A low level function that clears a range to zero.
///\tparam T The type.
///\param begin The first object in the range.
///\param n The number of objects.
///\param value The value to set the object's memory to.
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_set_range(volatile T* begin, size_t n, const char value)
{
memory_set(reinterpret_cast<volatile char*>(begin), n * sizeof(T), value);
}
//*****************************************************************************
/// A low level function that clears a range to zero.
///\tparam T The type.
///\param begin The first object in the range.
///\param end One past the last object in the range.
///\param value The value to set the object's memory to.
///\ingroup memory
//*****************************************************************************
template <typename T>
void memory_set_range(volatile T* begin, volatile T* end, const char value)
{
const size_t n = static_cast<size_t>(std::distance(begin, end));
memory_set_range(begin, n, value);
}
//*****************************************************************************
/// Base class for objects that require their memory to be wiped after use.
/// Erases the object's memory to zero.
/// Note: This may not work for multiply inherited objects.
///\tparam T The derived type.
///\ingroup memory
//*****************************************************************************
template <typename T>
struct wipe_on_destruct
{
~wipe_on_destruct()
{
memory_clear(static_cast<volatile T&>(*this));
}
};
}
#endif

View File

@ -38,8 +38,8 @@ SOFTWARE.
///\ingroup utilities
#define ETL_VERSION_MAJOR 14
#define ETL_VERSION_MINOR 22
#define ETL_VERSION_PATCH 1
#define ETL_VERSION_MINOR 23
#define ETL_VERSION_PATCH 0
#define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH)
#define ETL_VERSION_W ETL_WIDE_STRING(ETL_CONCAT(ETL_CONCAT(ETL_VERSION_MAJOR, ETL_VERSION_MINOR), ETL_VERSION_PATCH))

View File

@ -1,3 +1,8 @@
===============================================================================
14.23.0
Added an optional secure mode to strings so that unused space will be cleared to zero
and also cleared on destruction.
===============================================================================
14.22.1
Modified memory functions so that they will not be optimised away.

View File

@ -546,6 +546,52 @@ namespace
CHECK_EQUAL(0x00, data.d2);
}
//*************************************************************************
TEST(test_memory_clear_range_pointer_n)
{
struct Data
{
uint32_t d1;
char d2;
};
Data data[3] = { { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) } };
etl::memory_clear_range(data, 3);
CHECK_EQUAL(0x00000000, data[0].d1);
CHECK_EQUAL(0x00, data[0].d2);
CHECK_EQUAL(0x00000000, data[1].d1);
CHECK_EQUAL(0x00, data[1].d2);
CHECK_EQUAL(0x00000000, data[2].d1);
CHECK_EQUAL(0x00, data[2].d2);
}
//*************************************************************************
TEST(test_memory_clear_range_pointer_pointer)
{
struct Data
{
uint32_t d1;
char d2;
};
Data data[3] = { { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) } };
etl::memory_clear_range(std::begin(data), std::end(data));
CHECK_EQUAL(0x00000000, data[0].d1);
CHECK_EQUAL(0x00, data[0].d2);
CHECK_EQUAL(0x00000000, data[1].d1);
CHECK_EQUAL(0x00, data[1].d2);
CHECK_EQUAL(0x00000000, data[2].d1);
CHECK_EQUAL(0x00, data[2].d2);
}
//*************************************************************************
TEST(test_memory_set)
{
@ -562,5 +608,51 @@ namespace
CHECK_EQUAL(0x5A5A5A5A, data.d1);
CHECK_EQUAL(0x5A, data.d2);
}
//*************************************************************************
TEST(test_memory_set_range_pointer_n)
{
struct Data
{
uint32_t d1;
char d2;
};
Data data[3] = { { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) } };
etl::memory_set_range(data, 3, 0x5A);
CHECK_EQUAL(0x5A5A5A5A, data[0].d1);
CHECK_EQUAL(0x5A, data[0].d2);
CHECK_EQUAL(0x5A5A5A5A, data[1].d1);
CHECK_EQUAL(0x5A, data[1].d2);
CHECK_EQUAL(0x5A5A5A5A, data[2].d1);
CHECK_EQUAL(0x5A, data[2].d2);
}
//*************************************************************************
TEST(test_memory_set_range_pointer_pointer)
{
struct Data
{
uint32_t d1;
char d2;
};
Data data[3] = { { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) }, { 0xFFFFFFFF, char(0xFF) } };
etl::memory_set_range(std::begin(data), std::end(data), 0x5A);
CHECK_EQUAL(0x5A5A5A5A, data[0].d1);
CHECK_EQUAL(0x5A, data[0].d2);
CHECK_EQUAL(0x5A5A5A5A, data[1].d1);
CHECK_EQUAL(0x5A, data[1].d2);
CHECK_EQUAL(0x5A5A5A5A, data[2].d1);
CHECK_EQUAL(0x5A, data[2].d2);
}
};
}

View File

@ -3651,5 +3651,90 @@ namespace
text.clear_truncated();
CHECK(!text.truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_destructor)
{
char buffer[sizeof(Text)];
std::fill_n(buffer, sizeof(Text), 0);
::new (buffer) Text(STR("ABCDEF"));
Text& text = *reinterpret_cast<Text*>(buffer);
text.set_secure();
CHECK(Text(STR("ABCDEF")) == text);
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
// Destroy the text object.
text.~Text();
// Check there no non-zero values in the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_assign)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.assign(STR("ABC"));
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_erase)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.erase(pb + 2, pb + 5);
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_replace)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.replace(pb + 1, pb + 4, STR("G"));
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_clear)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.clear();
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
};
}

View File

@ -3650,5 +3650,90 @@ namespace
text.clear_truncated();
CHECK(!text.truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_destructor)
{
char buffer[sizeof(Text)];
std::fill_n(buffer, sizeof(Text), 0);
::new (buffer) Text(STR("ABCDEF"));
Text& text = *reinterpret_cast<Text*>(buffer);
text.set_secure();
CHECK(Text(STR("ABCDEF")) == text);
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
// Destroy the text object.
text.~Text();
// Check there no non-zero values in the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_assign)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.assign(STR("ABC"));
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_erase)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.erase(pb + 2, pb + 5);
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_replace)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.replace(pb + 1, pb + 4, STR("G"));
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_clear)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.clear();
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
};
}

View File

@ -3650,5 +3650,90 @@ namespace
text.clear_truncated();
CHECK(!text.truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_destructor)
{
char buffer[sizeof(Text)];
std::fill_n(buffer, sizeof(Text), 0);
::new (buffer) Text(STR("ABCDEF"));
Text& text = *reinterpret_cast<Text*>(buffer);
text.set_secure();
CHECK(Text(STR("ABCDEF")) == text);
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
// Destroy the text object.
text.~Text();
// Check there no non-zero values in the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_assign)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.assign(STR("ABC"));
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_erase)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.erase(pb + 2, pb + 5);
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_replace)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.replace(pb + 1, pb + 4, STR("G"));
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_clear)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.clear();
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
};
}

View File

@ -3650,5 +3650,90 @@ namespace
text.clear_truncated();
CHECK(!text.truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_destructor)
{
char buffer[sizeof(Text)];
std::fill_n(buffer, sizeof(Text), 0);
::new (buffer) Text(STR("ABCDEF"));
Text& text = *reinterpret_cast<Text*>(buffer);
text.set_secure();
CHECK(Text(STR("ABCDEF")) == text);
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
// Destroy the text object.
text.~Text();
// Check there no non-zero values in the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_assign)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.assign(STR("ABC"));
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_erase)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.erase(pb + 2, pb + 5);
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_replace)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.replace(pb + 1, pb + 4, STR("G"));
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(text.end(), pe, [](Text::value_type x) { return x != 0; }) == pe);
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_secure_after_clear)
{
Text text;
text.set_secure();
text.assign(STR("ABCDEF"));
Text::pointer pb = text.begin();
Text::pointer pe = text.end();
text.clear();
// Check there no non-zero values in the remainder of the string.
CHECK(std::find_if(pb, pe, [](Text::value_type x) { return x != 0; }) == pe);
}
};
}