Skip to content

Commit 1a9f9f7

Browse files
committed
Introduce SafeDbt to handle DB_DBT_MALLOC raii-style
This provides additional exception-safety and case handling for the proper freeing of the associated buffers.
1 parent 951a44e commit 1a9f9f7

File tree

2 files changed

+70
-40
lines changed

2 files changed

+70
-40
lines changed

src/wallet/db.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,45 @@ BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string&
247247
return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
248248
}
249249

250+
BerkeleyBatch::SafeDbt::SafeDbt(u_int32_t flags)
251+
{
252+
m_dbt.set_flags(flags);
253+
}
254+
255+
BerkeleyBatch::SafeDbt::SafeDbt(void *data, size_t size)
256+
: m_dbt(data, size)
257+
{
258+
}
259+
260+
BerkeleyBatch::SafeDbt::~SafeDbt()
261+
{
262+
if (m_dbt.get_data() != nullptr) {
263+
// Clear memory, e.g. in case it was a private key
264+
memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
265+
// under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
266+
// freed by the caller.
267+
// https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
268+
if (m_dbt.get_flags() & DB_DBT_MALLOC) {
269+
free(m_dbt.get_data());
270+
}
271+
}
272+
}
273+
274+
const void* BerkeleyBatch::SafeDbt::get_data() const
275+
{
276+
return m_dbt.get_data();
277+
}
278+
279+
u_int32_t BerkeleyBatch::SafeDbt::get_size() const
280+
{
281+
return m_dbt.get_size();
282+
}
283+
284+
BerkeleyBatch::SafeDbt::operator Dbt*()
285+
{
286+
return &m_dbt;
287+
}
288+
250289
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
251290
{
252291
std::string filename;

src/wallet/db.h

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,27 @@ class BerkeleyDatabase
166166
bool IsDummy() { return env == nullptr; }
167167
};
168168

169-
170169
/** RAII class that provides access to a Berkeley database */
171170
class BerkeleyBatch
172171
{
172+
/** RAII class that automatically cleanses its data on destruction */
173+
class SafeDbt final {
174+
Dbt m_dbt;
175+
176+
public:
177+
// construct Dbt with data or flags
178+
SafeDbt(u_int32_t flags = 0);
179+
SafeDbt(void *data, size_t size);
180+
~SafeDbt();
181+
182+
// delegate to Dbt
183+
const void* get_data() const;
184+
u_int32_t get_size() const;
185+
186+
// conversion operator to access the underlying Dbt
187+
operator Dbt*();
188+
};
189+
173190
protected:
174191
Db* pdb;
175192
std::string strFile;
@@ -197,7 +214,6 @@ class BerkeleyBatch
197214
/* verifies the database file */
198215
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
199216

200-
public:
201217
template <typename K, typename T>
202218
bool Read(const K& key, T& value)
203219
{
@@ -208,13 +224,11 @@ class BerkeleyBatch
208224
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
209225
ssKey.reserve(1000);
210226
ssKey << key;
211-
Dbt datKey(ssKey.data(), ssKey.size());
227+
SafeDbt datKey(ssKey.data(), ssKey.size());
212228

213229
// Read
214-
Dbt datValue;
215-
datValue.set_flags(DB_DBT_MALLOC);
216-
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
217-
memory_cleanse(datKey.get_data(), datKey.get_size());
230+
SafeDbt datValue(DB_DBT_MALLOC);
231+
int ret = pdb->get(activeTxn, datKey, datValue, 0);
218232
bool success = false;
219233
if (datValue.get_data() != nullptr) {
220234
// Unserialize value
@@ -225,10 +239,6 @@ class BerkeleyBatch
225239
} catch (const std::exception&) {
226240
// In this case success remains 'false'
227241
}
228-
229-
// Clear and free memory
230-
memory_cleanse(datValue.get_data(), datValue.get_size());
231-
free(datValue.get_data());
232242
}
233243
return ret == 0 && success;
234244
}
@@ -245,20 +255,16 @@ class BerkeleyBatch
245255
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
246256
ssKey.reserve(1000);
247257
ssKey << key;
248-
Dbt datKey(ssKey.data(), ssKey.size());
258+
SafeDbt datKey(ssKey.data(), ssKey.size());
249259

250260
// Value
251261
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
252262
ssValue.reserve(10000);
253263
ssValue << value;
254-
Dbt datValue(ssValue.data(), ssValue.size());
264+
SafeDbt datValue(ssValue.data(), ssValue.size());
255265

256266
// Write
257-
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
258-
259-
// Clear memory in case it was a private key
260-
memory_cleanse(datKey.get_data(), datKey.get_size());
261-
memory_cleanse(datValue.get_data(), datValue.get_size());
267+
int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
262268
return (ret == 0);
263269
}
264270

@@ -274,13 +280,10 @@ class BerkeleyBatch
274280
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
275281
ssKey.reserve(1000);
276282
ssKey << key;
277-
Dbt datKey(ssKey.data(), ssKey.size());
283+
SafeDbt datKey(ssKey.data(), ssKey.size());
278284

279285
// Erase
280-
int ret = pdb->del(activeTxn, &datKey, 0);
281-
282-
// Clear memory
283-
memory_cleanse(datKey.get_data(), datKey.get_size());
286+
int ret = pdb->del(activeTxn, datKey, 0);
284287
return (ret == 0 || ret == DB_NOTFOUND);
285288
}
286289

@@ -294,13 +297,10 @@ class BerkeleyBatch
294297
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
295298
ssKey.reserve(1000);
296299
ssKey << key;
297-
Dbt datKey(ssKey.data(), ssKey.size());
300+
SafeDbt datKey(ssKey.data(), ssKey.size());
298301

299302
// Exists
300-
int ret = pdb->exists(activeTxn, &datKey, 0);
301-
302-
// Clear memory
303-
memory_cleanse(datKey.get_data(), datKey.get_size());
303+
int ret = pdb->exists(activeTxn, datKey, 0);
304304
return (ret == 0);
305305
}
306306

@@ -318,11 +318,9 @@ class BerkeleyBatch
318318
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
319319
{
320320
// Read at cursor
321-
Dbt datKey;
322-
Dbt datValue;
323-
datKey.set_flags(DB_DBT_MALLOC);
324-
datValue.set_flags(DB_DBT_MALLOC);
325-
int ret = pcursor->get(&datKey, &datValue, DB_NEXT);
321+
SafeDbt datKey(DB_DBT_MALLOC);
322+
SafeDbt datValue(DB_DBT_MALLOC);
323+
int ret = pcursor->get(datKey, datValue, DB_NEXT);
326324
if (ret != 0)
327325
return ret;
328326
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
@@ -335,16 +333,9 @@ class BerkeleyBatch
335333
ssValue.SetType(SER_DISK);
336334
ssValue.clear();
337335
ssValue.write((char*)datValue.get_data(), datValue.get_size());
338-
339-
// Clear and free memory
340-
memory_cleanse(datKey.get_data(), datKey.get_size());
341-
memory_cleanse(datValue.get_data(), datValue.get_size());
342-
free(datKey.get_data());
343-
free(datValue.get_data());
344336
return 0;
345337
}
346338

347-
public:
348339
bool TxnBegin()
349340
{
350341
if (!pdb || activeTxn)

0 commit comments

Comments
 (0)