Skip to content

Commit

Permalink
lmdb: be sure to abort pending transactions in the correct order.
Browse files Browse the repository at this point in the history
If the LMDBBackend destructor is invoked while there are still pending
transactions, these need to be aborted, but in the reverse order of
their creation (i.e. abort the innermost transaction first).

The default destructor would abort them in a class field
declaration-dependent order, which may not match the actual cinematic.

We now remember which transaction is the innermost one, so that we can
abort them in the expected order.

This gets rid of "double free or corruption (top)" aborts with glibc,
and Address Sanitizer errors.
  • Loading branch information
miodvallat committed Feb 19, 2025
1 parent 085f2db commit 7b5e66a
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 0 deletions.
20 changes: 20 additions & 0 deletions modules/lmdbbackend/lmdbbackend.cc
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,23 @@ LMDBBackend::LMDBBackend(const std::string& suffix)
d_dolog = ::arg().mustDo("query-logging");
}

LMDBBackend::~LMDBBackend()
{
// LMDB internals require that, if we have multiple transactions active,
// we destroy them in the reverse order of their creation, thus we can't
// let the default destructor take care of d_rotxn and d_rwtxn.
if (d_txnorder) {
// RO transaction more recent than RW transaction
d_rotxn.reset();
d_rwtxn.reset();
}
else {
// RW transaction more recent than RO transaction
d_rwtxn.reset();
d_rotxn.reset();
}
}

namespace boost
{
namespace serialization
Expand Down Expand Up @@ -1073,6 +1090,7 @@ bool LMDBBackend::startTransaction(const DNSName& domain, int domain_id)
throw DBException("Attempt to start a transaction while one was open already");
}
d_rwtxn = getRecordsRWTransaction(real_id);
d_txnorder = false;

d_transactiondomain = domain;
d_transactiondomainid = real_id;
Expand Down Expand Up @@ -1443,6 +1461,7 @@ bool LMDBBackend::list(const DNSName& target, int /* id */, bool include_disable
}

d_rotxn = getRecordsROTransaction(di.id, d_rwtxn);
d_txnorder = true;
d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));

compoundOrdername co;
Expand Down Expand Up @@ -1503,6 +1522,7 @@ void LMDBBackend::lookup(const QType& type, const DNSName& qdomain, int zoneId,
}
// cout<<"get will look for "<<relqname<< " in zone "<<hunt<<" with id "<<zoneId<<" and type "<<type.toString()<<endl;
d_rotxn = getRecordsROTransaction(zoneId, d_rwtxn);
d_txnorder = true;

compoundOrdername co;
d_getcursor = std::make_shared<MDBROCursor>(d_rotxn->txn->getCursor(d_rotxn->db->dbi));
Expand Down
2 changes: 2 additions & 0 deletions modules/lmdbbackend/lmdbbackend.hh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class LMDBBackend : public DNSBackend
{
public:
explicit LMDBBackend(const string& suffix = "");
~LMDBBackend();

bool list(const DNSName& target, int id, bool include_disabled) override;

Expand Down Expand Up @@ -305,6 +306,7 @@ private:

shared_ptr<RecordsROTransaction> d_rotxn; // for lookup and list
shared_ptr<RecordsRWTransaction> d_rwtxn; // for feedrecord within begin/aborttransaction
bool d_txnorder{false}; // whether d_rotxn is more recent than d_rwtxn
std::shared_ptr<RecordsRWTransaction> getRecordsRWTransaction(uint32_t id);
std::shared_ptr<RecordsROTransaction> getRecordsROTransaction(uint32_t id, const std::shared_ptr<LMDBBackend::RecordsRWTransaction>& rwtxn = nullptr);
int genChangeDomain(const DNSName& domain, const std::function<void(DomainInfo&)>& func);
Expand Down

0 comments on commit 7b5e66a

Please sign in to comment.