Skip to content

Commit d44b01f

Browse files
committed
Merge #14268: Introduce SafeDbt to handle Dbt with free or memory_cleanse raii-style
4a86a0a Make SafeDbt DB_DBT_MALLOC on default initialization (Ben Woosley) 1a9f9f7 Introduce SafeDbt to handle DB_DBT_MALLOC raii-style (Ben Woosley) 951a44e 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
2 parents 19c60ca + 4a86a0a commit d44b01f

File tree

2 files changed

+73
-47
lines changed

2 files changed

+73
-47
lines changed

src/wallet/db.cpp

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

266+
BerkeleyBatch::SafeDbt::SafeDbt()
267+
{
268+
m_dbt.set_flags(DB_DBT_MALLOC);
269+
}
270+
271+
BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
272+
: m_dbt(data, size)
273+
{
274+
}
275+
276+
BerkeleyBatch::SafeDbt::~SafeDbt()
277+
{
278+
if (m_dbt.get_data() != nullptr) {
279+
// Clear memory, e.g. in case it was a private key
280+
memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
281+
// under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
282+
// freed by the caller.
283+
// https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
284+
if (m_dbt.get_flags() & DB_DBT_MALLOC) {
285+
free(m_dbt.get_data());
286+
}
287+
}
288+
}
289+
290+
const void* BerkeleyBatch::SafeDbt::get_data() const
291+
{
292+
return m_dbt.get_data();
293+
}
294+
295+
u_int32_t BerkeleyBatch::SafeDbt::get_size() const
296+
{
297+
return m_dbt.get_size();
298+
}
299+
300+
BerkeleyBatch::SafeDbt::operator Dbt*()
301+
{
302+
return &m_dbt;
303+
}
304+
266305
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
267306
{
268307
std::string filename;

src/wallet/db.h

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,29 @@ class BerkeleyDatabase
191191
bool IsDummy() { return env == nullptr; }
192192
};
193193

194-
195194
/** RAII class that provides access to a Berkeley database */
196195
class BerkeleyBatch
197196
{
197+
/** RAII class that automatically cleanses its data on destruction */
198+
class SafeDbt final
199+
{
200+
Dbt m_dbt;
201+
202+
public:
203+
// construct Dbt with internally-managed data
204+
SafeDbt();
205+
// construct Dbt with provided data
206+
SafeDbt(void* data, size_t size);
207+
~SafeDbt();
208+
209+
// delegate to Dbt
210+
const void* get_data() const;
211+
u_int32_t get_size() const;
212+
213+
// conversion operator to access the underlying Dbt
214+
operator Dbt*();
215+
};
216+
198217
protected:
199218
Db* pdb;
200219
std::string strFile;
@@ -222,7 +241,6 @@ class BerkeleyBatch
222241
/* verifies the database file */
223242
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
224243

225-
public:
226244
template <typename K, typename T>
227245
bool Read(const K& key, T& value)
228246
{
@@ -233,13 +251,11 @@ class BerkeleyBatch
233251
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
234252
ssKey.reserve(1000);
235253
ssKey << key;
236-
Dbt datKey(ssKey.data(), ssKey.size());
254+
SafeDbt datKey(ssKey.data(), ssKey.size());
237255

238256
// Read
239-
Dbt datValue;
240-
datValue.set_flags(DB_DBT_MALLOC);
241-
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
242-
memory_cleanse(datKey.get_data(), datKey.get_size());
257+
SafeDbt datValue;
258+
int ret = pdb->get(activeTxn, datKey, datValue, 0);
243259
bool success = false;
244260
if (datValue.get_data() != nullptr) {
245261
// Unserialize value
@@ -250,10 +266,6 @@ class BerkeleyBatch
250266
} catch (const std::exception&) {
251267
// In this case success remains 'false'
252268
}
253-
254-
// Clear and free memory
255-
memory_cleanse(datValue.get_data(), datValue.get_size());
256-
free(datValue.get_data());
257269
}
258270
return ret == 0 && success;
259271
}
@@ -270,20 +282,16 @@ class BerkeleyBatch
270282
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
271283
ssKey.reserve(1000);
272284
ssKey << key;
273-
Dbt datKey(ssKey.data(), ssKey.size());
285+
SafeDbt datKey(ssKey.data(), ssKey.size());
274286

275287
// Value
276288
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
277289
ssValue.reserve(10000);
278290
ssValue << value;
279-
Dbt datValue(ssValue.data(), ssValue.size());
291+
SafeDbt datValue(ssValue.data(), ssValue.size());
280292

281293
// Write
282-
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
283-
284-
// Clear memory in case it was a private key
285-
memory_cleanse(datKey.get_data(), datKey.get_size());
286-
memory_cleanse(datValue.get_data(), datValue.get_size());
294+
int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
287295
return (ret == 0);
288296
}
289297

@@ -299,13 +307,10 @@ class BerkeleyBatch
299307
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
300308
ssKey.reserve(1000);
301309
ssKey << key;
302-
Dbt datKey(ssKey.data(), ssKey.size());
310+
SafeDbt datKey(ssKey.data(), ssKey.size());
303311

304312
// Erase
305-
int ret = pdb->del(activeTxn, &datKey, 0);
306-
307-
// Clear memory
308-
memory_cleanse(datKey.get_data(), datKey.get_size());
313+
int ret = pdb->del(activeTxn, datKey, 0);
309314
return (ret == 0 || ret == DB_NOTFOUND);
310315
}
311316

@@ -319,13 +324,10 @@ class BerkeleyBatch
319324
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
320325
ssKey.reserve(1000);
321326
ssKey << key;
322-
Dbt datKey(ssKey.data(), ssKey.size());
327+
SafeDbt datKey(ssKey.data(), ssKey.size());
323328

324329
// Exists
325-
int ret = pdb->exists(activeTxn, &datKey, 0);
326-
327-
// Clear memory
328-
memory_cleanse(datKey.get_data(), datKey.get_size());
330+
int ret = pdb->exists(activeTxn, datKey, 0);
329331
return (ret == 0);
330332
}
331333

@@ -340,20 +342,12 @@ class BerkeleyBatch
340342
return pcursor;
341343
}
342344

343-
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
345+
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
344346
{
345347
// Read at cursor
346-
Dbt datKey;
347-
unsigned int fFlags = DB_NEXT;
348-
if (setRange) {
349-
datKey.set_data(ssKey.data());
350-
datKey.set_size(ssKey.size());
351-
fFlags = DB_SET_RANGE;
352-
}
353-
Dbt datValue;
354-
datKey.set_flags(DB_DBT_MALLOC);
355-
datValue.set_flags(DB_DBT_MALLOC);
356-
int ret = pcursor->get(&datKey, &datValue, fFlags);
348+
SafeDbt datKey;
349+
SafeDbt datValue;
350+
int ret = pcursor->get(datKey, datValue, DB_NEXT);
357351
if (ret != 0)
358352
return ret;
359353
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
@@ -366,16 +360,9 @@ class BerkeleyBatch
366360
ssValue.SetType(SER_DISK);
367361
ssValue.clear();
368362
ssValue.write((char*)datValue.get_data(), datValue.get_size());
369-
370-
// Clear and free memory
371-
memory_cleanse(datKey.get_data(), datKey.get_size());
372-
memory_cleanse(datValue.get_data(), datValue.get_size());
373-
free(datKey.get_data());
374-
free(datValue.get_data());
375363
return 0;
376364
}
377365

378-
public:
379366
bool TxnBegin()
380367
{
381368
if (!pdb || activeTxn)

0 commit comments

Comments
 (0)