mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 04:52:59 +01:00
Merge #14268: Introduce SafeDbt to handle Dbt with free or memory_cleanse raii-style
4a86a0acd9ac3ca392f0584a5fd079a856e5e4ba Make SafeDbt DB_DBT_MALLOC on default initialization (Ben Woosley) 1a9f9f7e5e2e73fb832f5b96ad7e9e57954f3f3c Introduce SafeDbt to handle DB_DBT_MALLOC raii-style (Ben Woosley) 951a44e9cd6cf2b8058244f3f95181c5ba683fdd Drop unused setRange arg to BerkeleyBatch::ReadAtCursor (Ben Woosley) Pull request description: This provides additional exception-safety and case handling for the proper freeing of the associated buffers. Tree-SHA512: a038d728290cdb3905e7d881608052a6675b6425729ceaf7cfe69a6e91c2ee293cdb01e4b695a20963459ffdd9d4a1f9a08b3c07b1b5ba1aa8590a8149f686db
This commit is contained in:
parent
6e91a22d80
commit
8b47451341
@ -286,6 +286,45 @@ BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string&
|
|||||||
return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
|
return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BerkeleyBatch::SafeDbt::SafeDbt()
|
||||||
|
{
|
||||||
|
m_dbt.set_flags(DB_DBT_MALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
|
||||||
|
: m_dbt(data, size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BerkeleyBatch::SafeDbt::~SafeDbt()
|
||||||
|
{
|
||||||
|
if (m_dbt.get_data() != nullptr) {
|
||||||
|
// Clear memory, e.g. in case it was a private key
|
||||||
|
memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
|
||||||
|
// under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
|
||||||
|
// freed by the caller.
|
||||||
|
// https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
|
||||||
|
if (m_dbt.get_flags() & DB_DBT_MALLOC) {
|
||||||
|
free(m_dbt.get_data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* BerkeleyBatch::SafeDbt::get_data() const
|
||||||
|
{
|
||||||
|
return m_dbt.get_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
u_int32_t BerkeleyBatch::SafeDbt::get_size() const
|
||||||
|
{
|
||||||
|
return m_dbt.get_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
BerkeleyBatch::SafeDbt::operator Dbt*()
|
||||||
|
{
|
||||||
|
return &m_dbt;
|
||||||
|
}
|
||||||
|
|
||||||
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
|
||||||
{
|
{
|
||||||
std::string filename;
|
std::string filename;
|
||||||
|
@ -205,10 +205,29 @@ private:
|
|||||||
bool IsDummy() { return env == nullptr; }
|
bool IsDummy() { return env == nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/** RAII class that provides access to a Berkeley database */
|
/** RAII class that provides access to a Berkeley database */
|
||||||
class BerkeleyBatch
|
class BerkeleyBatch
|
||||||
{
|
{
|
||||||
|
/** RAII class that automatically cleanses its data on destruction */
|
||||||
|
class SafeDbt final
|
||||||
|
{
|
||||||
|
Dbt m_dbt;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// construct Dbt with internally-managed data
|
||||||
|
SafeDbt();
|
||||||
|
// construct Dbt with provided data
|
||||||
|
SafeDbt(void* data, size_t size);
|
||||||
|
~SafeDbt();
|
||||||
|
|
||||||
|
// delegate to Dbt
|
||||||
|
const void* get_data() const;
|
||||||
|
u_int32_t get_size() const;
|
||||||
|
|
||||||
|
// conversion operator to access the underlying Dbt
|
||||||
|
operator Dbt*();
|
||||||
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Db* pdb;
|
Db* pdb;
|
||||||
std::string strFile;
|
std::string strFile;
|
||||||
@ -236,7 +255,6 @@ public:
|
|||||||
/* verifies the database file */
|
/* verifies the database file */
|
||||||
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
|
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename K, typename T>
|
template <typename K, typename T>
|
||||||
bool Read(const K& key, T& value)
|
bool Read(const K& key, T& value)
|
||||||
{
|
{
|
||||||
@ -247,13 +265,11 @@ public:
|
|||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(1000);
|
ssKey.reserve(1000);
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
Dbt datKey(ssKey.data(), ssKey.size());
|
SafeDbt datKey(ssKey.data(), ssKey.size());
|
||||||
|
|
||||||
// Read
|
// Read
|
||||||
Dbt datValue;
|
SafeDbt datValue;
|
||||||
datValue.set_flags(DB_DBT_MALLOC);
|
int ret = pdb->get(activeTxn, datKey, datValue, 0);
|
||||||
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
|
|
||||||
memory_cleanse(datKey.get_data(), datKey.get_size());
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (datValue.get_data() != nullptr) {
|
if (datValue.get_data() != nullptr) {
|
||||||
// Unserialize value
|
// Unserialize value
|
||||||
@ -264,10 +280,6 @@ public:
|
|||||||
} catch (const std::exception&) {
|
} catch (const std::exception&) {
|
||||||
// In this case success remains 'false'
|
// In this case success remains 'false'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear and free memory
|
|
||||||
memory_cleanse(datValue.get_data(), datValue.get_size());
|
|
||||||
free(datValue.get_data());
|
|
||||||
}
|
}
|
||||||
return ret == 0 && success;
|
return ret == 0 && success;
|
||||||
}
|
}
|
||||||
@ -284,20 +296,16 @@ public:
|
|||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(1000);
|
ssKey.reserve(1000);
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
Dbt datKey(ssKey.data(), ssKey.size());
|
SafeDbt datKey(ssKey.data(), ssKey.size());
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
|
||||||
ssValue.reserve(10000);
|
ssValue.reserve(10000);
|
||||||
ssValue << value;
|
ssValue << value;
|
||||||
Dbt datValue(ssValue.data(), ssValue.size());
|
SafeDbt datValue(ssValue.data(), ssValue.size());
|
||||||
|
|
||||||
// Write
|
// Write
|
||||||
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
|
int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
|
||||||
|
|
||||||
// Clear memory in case it was a private key
|
|
||||||
memory_cleanse(datKey.get_data(), datKey.get_size());
|
|
||||||
memory_cleanse(datValue.get_data(), datValue.get_size());
|
|
||||||
return (ret == 0);
|
return (ret == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,13 +321,10 @@ public:
|
|||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(1000);
|
ssKey.reserve(1000);
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
Dbt datKey(ssKey.data(), ssKey.size());
|
SafeDbt datKey(ssKey.data(), ssKey.size());
|
||||||
|
|
||||||
// Erase
|
// Erase
|
||||||
int ret = pdb->del(activeTxn, &datKey, 0);
|
int ret = pdb->del(activeTxn, datKey, 0);
|
||||||
|
|
||||||
// Clear memory
|
|
||||||
memory_cleanse(datKey.get_data(), datKey.get_size());
|
|
||||||
return (ret == 0 || ret == DB_NOTFOUND);
|
return (ret == 0 || ret == DB_NOTFOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,13 +338,10 @@ public:
|
|||||||
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
|
||||||
ssKey.reserve(1000);
|
ssKey.reserve(1000);
|
||||||
ssKey << key;
|
ssKey << key;
|
||||||
Dbt datKey(ssKey.data(), ssKey.size());
|
SafeDbt datKey(ssKey.data(), ssKey.size());
|
||||||
|
|
||||||
// Exists
|
// Exists
|
||||||
int ret = pdb->exists(activeTxn, &datKey, 0);
|
int ret = pdb->exists(activeTxn, datKey, 0);
|
||||||
|
|
||||||
// Clear memory
|
|
||||||
memory_cleanse(datKey.get_data(), datKey.get_size());
|
|
||||||
return (ret == 0);
|
return (ret == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,20 +356,12 @@ public:
|
|||||||
return pcursor;
|
return pcursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
|
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
|
||||||
{
|
{
|
||||||
// Read at cursor
|
// Read at cursor
|
||||||
Dbt datKey;
|
SafeDbt datKey;
|
||||||
unsigned int fFlags = DB_NEXT;
|
SafeDbt datValue;
|
||||||
if (setRange) {
|
int ret = pcursor->get(datKey, datValue, DB_NEXT);
|
||||||
datKey.set_data(ssKey.data());
|
|
||||||
datKey.set_size(ssKey.size());
|
|
||||||
fFlags = DB_SET_RANGE;
|
|
||||||
}
|
|
||||||
Dbt datValue;
|
|
||||||
datKey.set_flags(DB_DBT_MALLOC);
|
|
||||||
datValue.set_flags(DB_DBT_MALLOC);
|
|
||||||
int ret = pcursor->get(&datKey, &datValue, fFlags);
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
return ret;
|
return ret;
|
||||||
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
|
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
|
||||||
@ -380,16 +374,9 @@ public:
|
|||||||
ssValue.SetType(SER_DISK);
|
ssValue.SetType(SER_DISK);
|
||||||
ssValue.clear();
|
ssValue.clear();
|
||||||
ssValue.write((char*)datValue.get_data(), datValue.get_size());
|
ssValue.write((char*)datValue.get_data(), datValue.get_size());
|
||||||
|
|
||||||
// Clear and free memory
|
|
||||||
memory_cleanse(datKey.get_data(), datKey.get_size());
|
|
||||||
memory_cleanse(datValue.get_data(), datValue.get_size());
|
|
||||||
free(datKey.get_data());
|
|
||||||
free(datValue.get_data());
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
bool TxnBegin()
|
bool TxnBegin()
|
||||||
{
|
{
|
||||||
if (!pdb || activeTxn)
|
if (!pdb || activeTxn)
|
||||||
|
Loading…
Reference in New Issue
Block a user