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:
parent
eebdd3504a
commit
3897136029
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
1234
src/jrd/Mapping.cpp
1234
src/jrd/Mapping.cpp
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user