8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 04:43:03 +01:00

Fixed deadlock introduced by me half-a-year ago

This commit is contained in:
AlexPeshkoff 2017-05-19 17:21:04 +03:00
parent eebdd3504a
commit 3897136029
9 changed files with 824 additions and 646 deletions

View File

@ -193,7 +193,7 @@ namespace Firebird
return ptr;
}
private:
protected:
T* assign(T* const p)
{
if (ptr != p)
@ -215,6 +215,7 @@ namespace Firebird
return ptr;
}
private:
T* ptr;
};

View File

@ -10655,7 +10655,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd
if (ddlTriggerAction > 0)
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL);
DFW_post_work(transaction, dfw_clear_cache, NULL, MAPPING_CACHE);
DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::MAPPING_CACHE);
savePoint.release(); // everything is ok
}
@ -11047,7 +11047,7 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
}
// Invalidate system privileges cache
DFW_post_work(transaction, dfw_clear_cache, NULL, SYSTEM_PRIVILEGES_CACHE);
DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::SYSTEM_PRIVILEGES_CACHE);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -31,29 +31,137 @@
#include "../common/classes/alloc.h"
#include "../common/classes/fb_string.h"
#include "../common/classes/ClumpletReader.h"
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/Hash.h"
#include "../common/classes/GenericMap.h"
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/Monitoring.h"
#include "../jrd/scl.h"
namespace Jrd {
ULONG mapUser(const bool throwNotFoundError,
Firebird::string& name, Firebird::string& trusted_role, Firebird::string* auth_method,
Firebird::AuthReader::AuthBlock* newAuthBlock, UserId::Privileges* system_privileges,
const Firebird::AuthReader::AuthBlock& authBlock, const char* alias, const char* db,
const char* securityDb, const Firebird::string& sql_role,
Firebird::ICryptKeyCallback* cryptCb, Firebird::IAttachment* att);
// bits returned by mapUser
const ULONG MAPUSER_ERROR_NOT_THROWN = 1;
const ULONG MAPUSER_MAP_DOWN = 2;
class AuthWriter;
void clearMappingCache(const char* dbName, USHORT index);
// possible index values
const USHORT MAPPING_CACHE = 0;
const USHORT SYSTEM_PRIVILEGES_CACHE = 1;
class Mapping
{
public:
// constructor's flags
static const ULONG MAP_NO_FLAGS = 0;
static const ULONG MAP_THROW_NOT_FOUND = 1;
static const ULONG MAP_ERROR_HANDLER = 2;
Mapping(const ULONG flags, Firebird::ICryptKeyCallback* cryptCb);
void shutdownMappingIpc();
~Mapping();
// First provide the main input information - old auth block ...
void setAuthBlock(const Firebird::AuthReader::AuthBlock& authBlock);
// ... and finally specify additional information when available.
void setSqlRole(const Firebird::string& sqlRole);
void setDb(const char* alias, const char* db, Firebird::IAttachment* att);
void setSecurityDbAlias(const char* alias, const char* mainExpandedName);
void setErrorMessagesContextName(const char* context);
// This should be done before mapUser().
void needAuthMethod(Firebird::string& authMethod);
void needAuthBlock(Firebird::AuthReader::AuthBlock& newAuthBlock);
void needSystemPrivileges(UserId::Privileges& systemPrivileges);
// bits returned by mapUser
static const ULONG MAP_ERROR_NOT_THROWN = 1;
static const ULONG MAP_DOWN = 2;
// Now mapper is ready to perform main task and provide mapped login and trusted role.
ULONG mapUser(Firebird::string& name, Firebird::string& trustedRole);
// Do not keep mainHandle opened longer than needed
void clearMainHandle();
// possible clearCache() flags
static const USHORT MAPPING_CACHE = 0;
static const USHORT SYSTEM_PRIVILEGES_CACHE = 1;
// Helper statuc functions to perform cleanup & shutdown.
static void clearCache(const char* dbName, USHORT id);
static void shutdownIpc();
private:
const ULONG flags;
ULONG internalFlags;
Firebird::ICryptKeyCallback* cryptCallback;
Firebird::string* authMethod;
Firebird::AuthReader::AuthBlock* newAuthBlock;
UserId::Privileges* systemPrivileges;
const Firebird::AuthReader::AuthBlock* authBlock;
const char* mainAlias;
const char* mainDb;
const char* securityAlias;
const char* errorMessagesContext;
const Firebird::string* sqlRole;
public:
class DbHandle : public Firebird::RefPtr<Firebird::IAttachment>
{
public:
DbHandle();
void setAttachment(Firebird::IAttachment* att);
void clear();
bool attach(const char* aliasDb, Firebird::ICryptKeyCallback* cryptCb);
};
class Map;
typedef Firebird::HashTable<Map, Firebird::DEFAULT_HASH_SIZE, Map, Firebird::DefaultKeyValue<Map>, Map> MapHash;
class Map : public MapHash::Entry, public Firebird::GlobalStorage
{
public:
Map(const char* aUsing, const char* aPlugin, const char* aDb,
const char* aFromType, const char* aFrom,
SSHORT aRole, const char* aTo);
explicit Map(Firebird::AuthReader::Info& info); //type, name, plugin, secDb
static FB_SIZE_T hash(const Map& value, FB_SIZE_T hashSize);
Firebird::NoCaseString makeHashKey() const;
void trimAll();
virtual bool isEqual(const Map& k) const;
virtual Map* get();
Firebird::NoCaseString plugin, db, fromType, from, to;
bool toRole;
char usng;
};
class Cache : public MapHash, public Firebird::GlobalStorage, public Firebird::RefCounted
{
public:
Cache(const Firebird::NoCaseString& aliasDb, const Firebird::NoCaseString& db);
~Cache();
bool populate(Firebird::IAttachment *att);
void map(bool flagWild, Firebird::AuthReader::Info& info, AuthWriter& newBlock);
void search(Firebird::AuthReader::Info& info, const Map& from, AuthWriter& newBlock,
const Firebird::NoCaseString& originalUserName);
void varPlugin(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock);
void varDb(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock);
void varFrom(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock);
void varUsing(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock);
bool map4(bool flagWild, unsigned flagSet, Firebird::AuthReader& rdr,
Firebird::AuthReader::Info& info, AuthWriter& newBlock);
static void eraseEntry(Map* m);
public:
Firebird::Mutex populateMutex;
Firebird::NoCaseString alias, name;
bool dataFlag;
};
private:
Firebird::PathName secExpanded;
Firebird::RefPtr<Cache> dbCache, secCache;
DbHandle mainHandle;
void setInternalFlags();
bool ensureCachePresence(Firebird::RefPtr<Mapping::Cache>& cache, const char* alias,
const char* target, DbHandle& hdb, Firebird::ICryptKeyCallback* cryptCb, Cache* c2);
};
class GlobalMappingScan: public VirtualTableScan
{

View File

@ -2267,7 +2267,7 @@ static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_t
return true;
case 3:
clearMappingCache(dbb->dbb_filename.c_str(), work->dfw_id);
Mapping::clearCache(dbb->dbb_filename.c_str(), work->dfw_id);
break;
}

View File

@ -279,13 +279,15 @@ void JAttachment::addRef()
int JAttachment::release()
{
#ifdef DEV_BUILD
int r = --refCounter;
#ifdef DEBUG_ATT_COUNTERS
int x = --refCounter;
ReferenceCounterDebugger* my = ReferenceCounterDebugger::get(DEB_RLS_JATT);
const char* point = my ? my->rcd_point : " <Unknown> ";
fprintf(stderr, "Release from <%s> att %p cnt=%d\n", point, this, x);
if (x != 0)
return 1;
fprintf(stderr, "Release from <%s> att %p cnt=%d\n", point, this, r);
#endif
if (r != 0)
return r;
#else
if (--refCounter != 0)
return 1;
@ -1066,8 +1068,8 @@ static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tr
static void release_attachment(thread_db*, Jrd::Attachment*);
static void rollback(thread_db*, jrd_tra*, const bool);
static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0);
static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*,
const RefPtr<const Config>*, bool, IAttachment*, ICryptKeyCallback*);
static void getUserInfo(UserId&, const DatabaseOptions&, const char*,
const RefPtr<const Config>*, bool, Mapping& mapping);
static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM);
@ -1080,7 +1082,9 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas
m_filename(filename),
m_options(options)
{
getUserInfo(m_id, *m_options, m_filename, NULL, NULL, false, NULL, NULL);
Mapping mapping(Mapping::MAP_ERROR_HANDLER, NULL);
mapping.setAuthBlock(m_options->dpb_auth_block);
getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping);
}
@ -1359,12 +1363,16 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
PathName org_filename, expanded_name;
bool is_alias = false;
MutexEnsureUnlock guardDbInit(dbInitMutex, FB_FUNCTION);
Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, cryptCallback);
try
{
// Process database parameter block
options.get(dpb, dpb_length, invalid_client_SQL_dialect);
// And provide info about auth block to mapping
mapping.setAuthBlock(options.dpb_auth_block);
if (options.dpb_org_filename.hasData())
org_filename = options.dpb_org_filename;
else
@ -1396,6 +1404,9 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
if (ISC_check_if_remote(expanded_name, true))
ERR_post(Arg::Gds(isc_unavailable));
// We are ready to setup security database - before entering guardDbInit!!!
mapping.setSecurityDbAlias(config->getSecurityDatabase(), expanded_name.c_str());
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -1655,8 +1666,8 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
jAtt->getStable()->manualUnlock(attachment->att_flags);
try
{
getUserInfo(userId, options, filename, expanded_name.c_str(),
&config, false, jAtt, cryptCallback);
mapping.setDb(filename, expanded_name.c_str(), jAtt);
getUserInfo(userId, options, filename, &config, false, mapping);
}
catch(const Exception&)
{
@ -1950,6 +1961,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch
filename, options, false, user_status);
}
mapping.clearMainHandle();
unwindAttach(tdbb, ex, user_status, attachment, dbb, existingId);
}
}
@ -2444,12 +2456,14 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
PathName org_filename, expanded_name;
bool is_alias = false;
Firebird::RefPtr<const Config> config;
Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, cryptCallback);
try
{
// Process database parameter block
bool invalid_client_SQL_dialect = false;
options.get(dpb, dpb_length, invalid_client_SQL_dialect);
mapping.setAuthBlock(options.dpb_auth_block);
if (!invalid_client_SQL_dialect && options.dpb_sql_dialect == 99) {
options.dpb_sql_dialect = 0;
}
@ -2487,7 +2501,9 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
ERR_post(Arg::Gds(isc_unavailable));
// Check for correct credentials supplied
getUserInfo(userId, options, filename, NULL, &config, true, nullptr, cryptCallback);
mapping.setSecurityDbAlias(config->getSecurityDatabase(), expanded_name.c_str());
mapping.setDb(filename, expanded_name.c_str(), nullptr);
getUserInfo(userId, options, filename, &config, true, mapping);
#ifdef WIN_NT
guardDbInit.enter(); // Required to correctly expand name of just created database
@ -2812,6 +2828,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch
trace_failed_attach(attachment ? attachment->att_trace_manager : NULL,
filename, options, true, user_status);
mapping.clearMainHandle();
unwindAttach(tdbb, ex, user_status, attachment, dbb, false);
}
}
@ -4183,7 +4200,7 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const
// Do not put it into separate shutdown thread - during shutdown of TraceManager
// PluginManager wants to lock a mutex, which is sometimes already locked in current thread
TraceManager::shutdown();
shutdownMappingIpc();
Mapping::shutdownIpc();
}
catch (const Exception& ex)
{
@ -7338,9 +7355,8 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status
@param cryptCb
**/
static void getUserInfo(UserId& user, const DatabaseOptions& options,
const char* aliasName, const char* dbName, const RefPtr<const Config>* config, bool creating,
IAttachment* iAtt, ICryptKeyCallback* cryptCb)
static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName,
const RefPtr<const Config>* config, bool creating, Mapping& mapping)
{
bool wheel = false;
int id = -1, group = -1; // CVC: This var contained trash
@ -7366,9 +7382,10 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options,
}
else if (options.dpb_auth_block.hasData())
{
if (mapUser(true, name, trusted_role, &auth_method, &user.usr_auth_block, NULL,
options.dpb_auth_block, aliasName, dbName,
(config ? (*config)->getSecurityDatabase() : NULL), "", cryptCb, iAtt) & MAPUSER_MAP_DOWN)
mapping.needAuthMethod(auth_method);
mapping.needAuthBlock(user.usr_auth_block);
if (mapping.mapUser(name, trusted_role) & Mapping::MAP_DOWN)
{
user.setFlag(USR_mapdown);
}

View File

@ -727,14 +727,20 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
{
if (svc_auth_block.hasData())
{
// remote connection - use svc_auth_block
PathName dummy;
RefPtr<const Config> config;
expandDatabaseName(svc_expected_db, dummy, &config);
Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, svc_crypt_callback);
mapping.needAuthBlock(svc_auth_block);
mapping.setAuthBlock(svc_auth_block);
mapping.setErrorMessagesContextName("services manager");
mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr);
string trusted_role;
mapUser(true, svc_username, trusted_role, NULL, &svc_auth_block, NULL,
svc_auth_block, "services manager", NULL, config->getSecurityDatabase(), "",
svc_crypt_callback, NULL);
mapping.mapUser(svc_username, trusted_role);
trusted_role.upper();
svc_trusted_role = trusted_role == ADMIN_ROLE;
}

View File

@ -392,7 +392,7 @@ void TRA_commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_flag
status_exception::raise(&st);
secContext->tra = NULL;
clearMappingCache(tdbb->getDatabase()->dbb_config->getSecurityDatabase(), MAPPING_CACHE);
Mapping::clearCache(tdbb->getDatabase()->dbb_config->getSecurityDatabase(), Mapping::MAPPING_CACHE);
transaction->eraseSecDbContext();
}

View File

@ -260,15 +260,19 @@ void TraceManager::update_session(const TraceSession& session)
Database* dbb = attachment->att_database;
fb_assert(dbb);
mapResult = mapUser(false, s_user, t_role, NULL, NULL, &priv, session.ses_auth,
attachment->att_filename.c_str(), dbb->dbb_filename.c_str(),
dbb->dbb_config->getSecurityDatabase(), session.ses_role,
dbb->dbb_provider->getCryptCallback(), attachment->getInterface());
Mapping mapping(Mapping::MAP_NO_FLAGS, dbb->dbb_provider->getCryptCallback());
mapping.needSystemPrivileges(priv);
mapping.setAuthBlock(session.ses_auth);
mapping.setSqlRole(session.ses_role);
mapping.setSecurityDbAlias(dbb->dbb_config->getSecurityDatabase(), dbb->dbb_filename.c_str());
mapping.setDb(attachment->att_filename.c_str(), dbb->dbb_filename.c_str(),
attachment->getInterface());
mapResult = mapping.mapUser(s_user, t_role);
}
if (session.ses_auth.hasData())
{
if (mapResult & MAPUSER_ERROR_NOT_THROWN)
if (mapResult & Mapping::MAP_ERROR_NOT_THROWN)
return; // Error in mapUser() means missing context
t_role.upper();
@ -291,9 +295,13 @@ void TraceManager::update_session(const TraceSession& session)
RefPtr<const Config> config;
expandDatabaseName(service->getExpectedDb(), dummy, &config);
if (mapUser(false, s_user, t_role, NULL, NULL, NULL, session.ses_auth, "services manager",
NULL, config->getSecurityDatabase(), session.ses_role, service->getCryptCallback(),
NULL) & MAPUSER_ERROR_NOT_THROWN)
Mapping mapping(Mapping::MAP_NO_FLAGS, service->getCryptCallback());
mapping.setAuthBlock(session.ses_auth);
mapping.setErrorMessagesContextName("services manager");
mapping.setSqlRole(session.ses_role);
mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr);
if (mapping.mapUser(s_user, t_role) & Mapping::MAP_ERROR_NOT_THROWN)
{
// Error in mapUser() means missing context, therefore...
return;