Skip to content

Commit ccf507f

Browse files
laanwjdzutto
authored andcommitted
Merge bitcoin#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
1 parent eb0a3d8 commit ccf507f

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
@@ -286,6 +286,45 @@ BerkeleyEnvironment::VerifyResult BerkeleyEnvironment::Verify(const std::string&
286286
return (fRecovered ? VerifyResult::RECOVER_OK : VerifyResult::RECOVER_FAIL);
287287
}
288288

289+
BerkeleyBatch::SafeDbt::SafeDbt()
290+
{
291+
m_dbt.set_flags(DB_DBT_MALLOC);
292+
}
293+
294+
BerkeleyBatch::SafeDbt::SafeDbt(void* data, size_t size)
295+
: m_dbt(data, size)
296+
{
297+
}
298+
299+
BerkeleyBatch::SafeDbt::~SafeDbt()
300+
{
301+
if (m_dbt.get_data() != nullptr) {
302+
// Clear memory, e.g. in case it was a private key
303+
memory_cleanse(m_dbt.get_data(), m_dbt.get_size());
304+
// under DB_DBT_MALLOC, data is malloced by the Dbt, but must be
305+
// freed by the caller.
306+
// https://docs.oracle.com/cd/E17275_01/html/api_reference/C/dbt.html
307+
if (m_dbt.get_flags() & DB_DBT_MALLOC) {
308+
free(m_dbt.get_data());
309+
}
310+
}
311+
}
312+
313+
const void* BerkeleyBatch::SafeDbt::get_data() const
314+
{
315+
return m_dbt.get_data();
316+
}
317+
318+
u_int32_t BerkeleyBatch::SafeDbt::get_size() const
319+
{
320+
return m_dbt.get_size();
321+
}
322+
323+
BerkeleyBatch::SafeDbt::operator Dbt*()
324+
{
325+
return &m_dbt;
326+
}
327+
289328
bool BerkeleyBatch::Recover(const fs::path& file_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& newFilename)
290329
{
291330
std::string filename;

src/wallet/db.h

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

208-
209208
/** RAII class that provides access to a Berkeley database */
210209
class BerkeleyBatch
211210
{
211+
/** RAII class that automatically cleanses its data on destruction */
212+
class SafeDbt final
213+
{
214+
Dbt m_dbt;
215+
216+
public:
217+
// construct Dbt with internally-managed data
218+
SafeDbt();
219+
// construct Dbt with provided data
220+
SafeDbt(void* data, size_t size);
221+
~SafeDbt();
222+
223+
// delegate to Dbt
224+
const void* get_data() const;
225+
u_int32_t get_size() const;
226+
227+
// conversion operator to access the underlying Dbt
228+
operator Dbt*();
229+
};
230+
212231
protected:
213232
Db* pdb;
214233
std::string strFile;
@@ -236,7 +255,6 @@ class BerkeleyBatch
236255
/* verifies the database file */
237256
static bool VerifyDatabaseFile(const fs::path& file_path, std::string& warningStr, std::string& errorStr, BerkeleyEnvironment::recoverFunc_type recoverFunc);
238257

239-
public:
240258
template <typename K, typename T>
241259
bool Read(const K& key, T& value)
242260
{
@@ -247,13 +265,11 @@ class BerkeleyBatch
247265
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
248266
ssKey.reserve(1000);
249267
ssKey << key;
250-
Dbt datKey(ssKey.data(), ssKey.size());
268+
SafeDbt datKey(ssKey.data(), ssKey.size());
251269

252270
// Read
253-
Dbt datValue;
254-
datValue.set_flags(DB_DBT_MALLOC);
255-
int ret = pdb->get(activeTxn, &datKey, &datValue, 0);
256-
memory_cleanse(datKey.get_data(), datKey.get_size());
271+
SafeDbt datValue;
272+
int ret = pdb->get(activeTxn, datKey, datValue, 0);
257273
bool success = false;
258274
if (datValue.get_data() != nullptr) {
259275
// Unserialize value
@@ -264,10 +280,6 @@ class BerkeleyBatch
264280
} catch (const std::exception&) {
265281
// In this case success remains 'false'
266282
}
267-
268-
// Clear and free memory
269-
memory_cleanse(datValue.get_data(), datValue.get_size());
270-
free(datValue.get_data());
271283
}
272284
return ret == 0 && success;
273285
}
@@ -284,20 +296,16 @@ class BerkeleyBatch
284296
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
285297
ssKey.reserve(1000);
286298
ssKey << key;
287-
Dbt datKey(ssKey.data(), ssKey.size());
299+
SafeDbt datKey(ssKey.data(), ssKey.size());
288300

289301
// Value
290302
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
291303
ssValue.reserve(10000);
292304
ssValue << value;
293-
Dbt datValue(ssValue.data(), ssValue.size());
305+
SafeDbt datValue(ssValue.data(), ssValue.size());
294306

295307
// Write
296-
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
297-
298-
// Clear memory in case it was a private key
299-
memory_cleanse(datKey.get_data(), datKey.get_size());
300-
memory_cleanse(datValue.get_data(), datValue.get_size());
308+
int ret = pdb->put(activeTxn, datKey, datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
301309
return (ret == 0);
302310
}
303311

@@ -313,13 +321,10 @@ class BerkeleyBatch
313321
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
314322
ssKey.reserve(1000);
315323
ssKey << key;
316-
Dbt datKey(ssKey.data(), ssKey.size());
324+
SafeDbt datKey(ssKey.data(), ssKey.size());
317325

318326
// Erase
319-
int ret = pdb->del(activeTxn, &datKey, 0);
320-
321-
// Clear memory
322-
memory_cleanse(datKey.get_data(), datKey.get_size());
327+
int ret = pdb->del(activeTxn, datKey, 0);
323328
return (ret == 0 || ret == DB_NOTFOUND);
324329
}
325330

@@ -333,13 +338,10 @@ class BerkeleyBatch
333338
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
334339
ssKey.reserve(1000);
335340
ssKey << key;
336-
Dbt datKey(ssKey.data(), ssKey.size());
341+
SafeDbt datKey(ssKey.data(), ssKey.size());
337342

338343
// Exists
339-
int ret = pdb->exists(activeTxn, &datKey, 0);
340-
341-
// Clear memory
342-
memory_cleanse(datKey.get_data(), datKey.get_size());
344+
int ret = pdb->exists(activeTxn, datKey, 0);
343345
return (ret == 0);
344346
}
345347

@@ -354,20 +356,12 @@ class BerkeleyBatch
354356
return pcursor;
355357
}
356358

357-
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue, bool setRange = false)
359+
int ReadAtCursor(Dbc* pcursor, CDataStream& ssKey, CDataStream& ssValue)
358360
{
359361
// Read at cursor
360-
Dbt datKey;
361-
unsigned int fFlags = DB_NEXT;
362-
if (setRange) {
363-
datKey.set_data(ssKey.data());
364-
datKey.set_size(ssKey.size());
365-
fFlags = DB_SET_RANGE;
366-
}
367-
Dbt datValue;
368-
datKey.set_flags(DB_DBT_MALLOC);
369-
datValue.set_flags(DB_DBT_MALLOC);
370-
int ret = pcursor->get(&datKey, &datValue, fFlags);
362+
SafeDbt datKey;
363+
SafeDbt datValue;
364+
int ret = pcursor->get(datKey, datValue, DB_NEXT);
371365
if (ret != 0)
372366
return ret;
373367
else if (datKey.get_data() == nullptr || datValue.get_data() == nullptr)
@@ -380,16 +374,9 @@ class BerkeleyBatch
380374
ssValue.SetType(SER_DISK);
381375
ssValue.clear();
382376
ssValue.write((char*)datValue.get_data(), datValue.get_size());
383-
384-
// Clear and free memory
385-
memory_cleanse(datKey.get_data(), datKey.get_size());
386-
memory_cleanse(datValue.get_data(), datValue.get_size());
387-
free(datKey.get_data());
388-
free(datValue.get_data());
389377
return 0;
390378
}
391379

392-
public:
393380
bool TxnBegin()
394381
{
395382
if (!pdb || activeTxn)

0 commit comments

Comments
 (0)