mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:43:04 +01:00
Fixed CORE-2778: SegFault in services when using same handle in different threads
This commit is contained in:
parent
735a8383d1
commit
5a5489ab40
@ -223,8 +223,10 @@ namespace
|
|||||||
|
|
||||||
inline void validateHandle(Service* service)
|
inline void validateHandle(Service* service)
|
||||||
{
|
{
|
||||||
if (!service->checkHandle())
|
if (service && service->checkHandle())
|
||||||
status_exception::raise(Arg::Gds(isc_bad_svc_handle));
|
return;
|
||||||
|
|
||||||
|
status_exception::raise(Arg::Gds(isc_bad_svc_handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
class AttachmentHolder
|
class AttachmentHolder
|
||||||
|
119
src/jrd/svc.cpp
119
src/jrd/svc.cpp
@ -271,11 +271,11 @@ namespace {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Generic mutex to synchronize services
|
// Generic mutex to synchronize services
|
||||||
GlobalPtr<Mutex> svc_mutex;
|
GlobalPtr<Mutex> globalServicesMutex;
|
||||||
|
|
||||||
// All that we need to shutdown service threads when shutdown in progress
|
// All that we need to shutdown service threads when shutdown in progress
|
||||||
typedef Array<Jrd::Service*> AllServices;
|
typedef Array<Jrd::Service*> AllServices;
|
||||||
GlobalPtr<AllServices> allServices; // protected by svc_mutex
|
GlobalPtr<AllServices> allServices; // protected by globalServicesMutex
|
||||||
volatile bool svcShutdown = false;
|
volatile bool svcShutdown = false;
|
||||||
|
|
||||||
void put_status_arg(ISC_STATUS*& status, const MsgFormat::safe_cell& value)
|
void put_status_arg(ISC_STATUS*& status, const MsgFormat::safe_cell& value)
|
||||||
@ -302,6 +302,46 @@ namespace {
|
|||||||
|
|
||||||
using namespace Jrd;
|
using namespace Jrd;
|
||||||
|
|
||||||
|
Service::ExistenceGuard::ExistenceGuard(Service* s)
|
||||||
|
: svc(s), locked(false)
|
||||||
|
{
|
||||||
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
|
|
||||||
|
if (! svc->locateInAllServices())
|
||||||
|
{
|
||||||
|
// Service is so old that it's even missing in allSevrices array
|
||||||
|
Arg::Gds(isc_bad_svc_handle).raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (svc->svc_flags & SVC_detached)
|
||||||
|
{
|
||||||
|
// Service was already detached
|
||||||
|
Arg::Gds(isc_bad_svc_handle).raise();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appears we have correct handle, lock it to make sure service exists
|
||||||
|
// for our lifetime
|
||||||
|
svc->svc_existence_lock.enter();
|
||||||
|
fb_assert(!svc->svc_current_guard);
|
||||||
|
svc->svc_current_guard = this;
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Service::ExistenceGuard::~ExistenceGuard()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Service::ExistenceGuard::release()
|
||||||
|
{
|
||||||
|
if (locked)
|
||||||
|
{
|
||||||
|
locked = false;
|
||||||
|
svc->svc_current_guard = NULL;
|
||||||
|
svc->svc_existence_lock.leave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Service::parseSwitches()
|
void Service::parseSwitches()
|
||||||
{
|
{
|
||||||
svc_parsed_sw = svc_switches;
|
svc_parsed_sw = svc_switches;
|
||||||
@ -388,7 +428,7 @@ void Service::started()
|
|||||||
{
|
{
|
||||||
if (!(svc_flags & SVC_evnt_fired))
|
if (!(svc_flags & SVC_evnt_fired))
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
svc_flags |= SVC_evnt_fired;
|
svc_flags |= SVC_evnt_fired;
|
||||||
svcStart.release();
|
svcStart.release();
|
||||||
}
|
}
|
||||||
@ -731,7 +771,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
|
|||||||
svc_trusted_login(getPool()), svc_trusted_role(false), svc_uses_security_database(false),
|
svc_trusted_login(getPool()), svc_trusted_role(false), svc_uses_security_database(false),
|
||||||
svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()),
|
svc_switches(getPool()), svc_perm_sw(getPool()), svc_address_path(getPool()),
|
||||||
svc_network_protocol(getPool()), svc_remote_address(getPool()), svc_remote_process(getPool()),
|
svc_network_protocol(getPool()), svc_remote_address(getPool()), svc_remote_process(getPool()),
|
||||||
svc_remote_pid(0)
|
svc_remote_pid(0), svc_current_guard(NULL)
|
||||||
{
|
{
|
||||||
svc_trace_manager = NULL;
|
svc_trace_manager = NULL;
|
||||||
memset(svc_status, 0, sizeof svc_status);
|
memset(svc_status, 0, sizeof svc_status);
|
||||||
@ -739,7 +779,7 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d
|
|||||||
|
|
||||||
{ // scope
|
{ // scope
|
||||||
// Account service block in global array
|
// Account service block in global array
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
checkForShutdown();
|
checkForShutdown();
|
||||||
allServices->add(this);
|
allServices->add(this);
|
||||||
}
|
}
|
||||||
@ -950,6 +990,8 @@ static THREAD_ENTRY_DECLARE svcShutdownThread(THREAD_ENTRY_PARAM)
|
|||||||
|
|
||||||
void Service::detach()
|
void Service::detach()
|
||||||
{
|
{
|
||||||
|
ExistenceGuard guard(this);
|
||||||
|
|
||||||
// save it cause after call to finish() we can't access class members any more
|
// save it cause after call to finish() we can't access class members any more
|
||||||
const bool localDoShutdown = svc_do_shutdown;
|
const bool localDoShutdown = svc_do_shutdown;
|
||||||
|
|
||||||
@ -978,30 +1020,53 @@ Service::~Service()
|
|||||||
|
|
||||||
delete svc_trace_manager;
|
delete svc_trace_manager;
|
||||||
svc_trace_manager = NULL;
|
svc_trace_manager = NULL;
|
||||||
|
|
||||||
|
if (svc_current_guard)
|
||||||
|
{
|
||||||
|
svc_current_guard->release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Service::removeFromAllServices()
|
void Service::removeFromAllServices()
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
AllServices& all(allServices);
|
|
||||||
|
|
||||||
for (unsigned int pos = 0; pos < all.getCount(); ++pos)
|
size_t pos;
|
||||||
|
if (locateInAllServices(&pos))
|
||||||
{
|
{
|
||||||
if (all[pos] == this)
|
allServices->remove(pos);
|
||||||
{
|
return;
|
||||||
all.remove(pos);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fb_assert(false);
|
fb_assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Service::locateInAllServices(size_t* posPtr)
|
||||||
|
{
|
||||||
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
|
AllServices& all(allServices);
|
||||||
|
|
||||||
|
for (size_t pos = 0; pos < all.getCount(); ++pos)
|
||||||
|
{
|
||||||
|
if (all[pos] == this)
|
||||||
|
{
|
||||||
|
if (posPtr)
|
||||||
|
{
|
||||||
|
*posPtr = pos;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ULONG Service::totalCount()
|
ULONG Service::totalCount()
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
return allServices->getCount();
|
return allServices->getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1010,7 +1075,7 @@ bool Service::checkForShutdown()
|
|||||||
{
|
{
|
||||||
if (svcShutdown)
|
if (svcShutdown)
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
|
|
||||||
if (svc_flags & SVC_shutdown)
|
if (svc_flags & SVC_shutdown)
|
||||||
{
|
{
|
||||||
@ -1030,16 +1095,16 @@ void Service::shutdownServices()
|
|||||||
{
|
{
|
||||||
svcShutdown = true;
|
svcShutdown = true;
|
||||||
|
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
AllServices& all(allServices);
|
AllServices& all(allServices);
|
||||||
|
|
||||||
for (unsigned int pos = 0; pos < all.getCount(); )
|
for (unsigned int pos = 0; pos < all.getCount(); )
|
||||||
{
|
{
|
||||||
if (all[pos]->svc_flags & SVC_thd_running)
|
if (all[pos]->svc_flags & SVC_thd_running)
|
||||||
{
|
{
|
||||||
svc_mutex->leave();
|
globalServicesMutex->leave();
|
||||||
THD_sleep(1);
|
THD_sleep(1);
|
||||||
svc_mutex->enter();
|
globalServicesMutex->enter();
|
||||||
pos = 0;
|
pos = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1057,6 +1122,8 @@ ISC_STATUS Service::query2(thread_db* tdbb,
|
|||||||
USHORT buffer_length,
|
USHORT buffer_length,
|
||||||
UCHAR* info)
|
UCHAR* info)
|
||||||
{
|
{
|
||||||
|
ExistenceGuard guard(this);
|
||||||
|
|
||||||
UCHAR item;
|
UCHAR item;
|
||||||
UCHAR buffer[MAXPATHLEN];
|
UCHAR buffer[MAXPATHLEN];
|
||||||
USHORT l, length, version, get_flags;
|
USHORT l, length, version, get_flags;
|
||||||
@ -1545,6 +1612,8 @@ void Service::query(USHORT send_item_length,
|
|||||||
USHORT buffer_length,
|
USHORT buffer_length,
|
||||||
UCHAR* info)
|
UCHAR* info)
|
||||||
{
|
{
|
||||||
|
ExistenceGuard guard(this);
|
||||||
|
|
||||||
UCHAR item, *p;
|
UCHAR item, *p;
|
||||||
UCHAR buffer[256];
|
UCHAR buffer[256];
|
||||||
USHORT l, length, version, get_flags;
|
USHORT l, length, version, get_flags;
|
||||||
@ -1927,6 +1996,8 @@ void Service::query(USHORT send_item_length,
|
|||||||
|
|
||||||
void Service::start(USHORT spb_length, const UCHAR* spb_data)
|
void Service::start(USHORT spb_length, const UCHAR* spb_data)
|
||||||
{
|
{
|
||||||
|
ExistenceGuard guard(this);
|
||||||
|
|
||||||
ThreadIdHolder holdId(svc_thread_strings);
|
ThreadIdHolder holdId(svc_thread_strings);
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -1952,8 +2023,8 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
|
|||||||
status_exception::raise(Arg::Gds(isc_bad_spb_form));
|
status_exception::raise(Arg::Gds(isc_bad_spb_form));
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // scope for locked svc_mutex
|
{ // scope for locked globalServicesMutex
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
|
|
||||||
if (svc_flags & SVC_thd_running) {
|
if (svc_flags & SVC_thd_running) {
|
||||||
status_exception::raise(Arg::Gds(isc_svc_in_use) << Arg::Str(serv->serv_name));
|
status_exception::raise(Arg::Gds(isc_svc_in_use) << Arg::Str(serv->serv_name));
|
||||||
@ -2059,7 +2130,7 @@ void Service::start(USHORT spb_length, const UCHAR* spb_data)
|
|||||||
if (serv->serv_thd)
|
if (serv->serv_thd)
|
||||||
{
|
{
|
||||||
{ // scope
|
{ // scope
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
svc_flags &= ~SVC_evnt_fired;
|
svc_flags &= ~SVC_evnt_fired;
|
||||||
svc_flags |= SVC_thd_running;
|
svc_flags |= SVC_thd_running;
|
||||||
}
|
}
|
||||||
@ -2258,7 +2329,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
|
|||||||
*return_length = 0;
|
*return_length = 0;
|
||||||
|
|
||||||
{ // scope
|
{ // scope
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
svc_flags &= ~SVC_timeout;
|
svc_flags &= ~SVC_timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2282,7 +2353,7 @@ void Service::get(UCHAR* buffer, USHORT length, USHORT flags, USHORT timeout, US
|
|||||||
#endif
|
#endif
|
||||||
if (timeout && elapsed_time >= timeout)
|
if (timeout && elapsed_time >= timeout)
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
svc_flags |= SVC_timeout;
|
svc_flags |= SVC_timeout;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2323,7 +2394,7 @@ void Service::finish(USHORT flag)
|
|||||||
{
|
{
|
||||||
if (flag == SVC_finished || flag == SVC_detached)
|
if (flag == SVC_finished || flag == SVC_detached)
|
||||||
{
|
{
|
||||||
MutexLockGuard guard(svc_mutex);
|
MutexLockGuard guard(globalServicesMutex);
|
||||||
|
|
||||||
svc_flags |= flag;
|
svc_flags |= flag;
|
||||||
if (! (svc_flags & SVC_thd_running))
|
if (! (svc_flags & SVC_thd_running))
|
||||||
|
@ -182,6 +182,8 @@ private:
|
|||||||
// Service must have private destructor, called from finish
|
// Service must have private destructor, called from finish
|
||||||
// when both (server and client) threads are finished
|
// when both (server and client) threads are finished
|
||||||
~Service();
|
~Service();
|
||||||
|
// Find current service in global services list
|
||||||
|
bool locateInAllServices(size_t* posPtr = NULL);
|
||||||
// Detach self from global services list
|
// Detach self from global services list
|
||||||
void removeFromAllServices();
|
void removeFromAllServices();
|
||||||
// The only service, implemented internally
|
// The only service, implemented internally
|
||||||
@ -279,6 +281,21 @@ public:
|
|||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
StatusStringsHelper svc_thread_strings;
|
StatusStringsHelper svc_thread_strings;
|
||||||
|
|
||||||
|
//Service existence guard
|
||||||
|
class ExistenceGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExistenceGuard(Service* svc);
|
||||||
|
~ExistenceGuard();
|
||||||
|
void release();
|
||||||
|
private:
|
||||||
|
Service* svc;
|
||||||
|
bool locked;
|
||||||
|
};
|
||||||
|
friend class ExistenceGuard;
|
||||||
|
Firebird::Mutex svc_existence_lock;
|
||||||
|
ExistenceGuard* svc_current_guard;
|
||||||
};
|
};
|
||||||
|
|
||||||
} //namespace Jrd
|
} //namespace Jrd
|
||||||
|
Loading…
Reference in New Issue
Block a user