Skip to content

Commit

Permalink
Backport improvement #7213 : Use Windows private namespace for kernel…
Browse files Browse the repository at this point in the history
… objects used in server-to-server IPC.

Backport fix for bug #7535 : High CPU usage connect to Firebird 3 database using Firebird 4 Classic and SuperClassic service.
Implement new setting UseLegacyKernelObjectsNames for backward compatibility.
Preserve Win32 build compatibility with WinXP.
  • Loading branch information
hvlad committed Apr 7, 2023
1 parent c50ed20 commit 6cb2297
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 12 deletions.
20 changes: 20 additions & 0 deletions builds/install/misc/firebird.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,26 @@
#
#RemotePipeName = interbas

# ----------------------------
# The way of how to create names for named Windows kernel objects, such as
# events, memory mapped files, etc. Since v4.0.3 Firebird creates its named
# kernel objects in private namespace. It allows to interact processes within
# different Windows sessions, such as user session and system session. Also,
# it uses engine version in some shared event names - it allows single process
# to host Firebird engines of different versions.
#
# The legacy names is for backward compatibility only, it allows simultaneous
# run of Firebird processes of newly (>= v4.0.3) and older (< 4.0.3)
# subreleases of version 4.
#
# The setting is global.
# It is used for backward compatibility only and will not be present in the
# future Firebird versions.
#
# Type: boolean
#
#UseLegacyKernelObjectsNames = false


# ============================
# Settings for Unix/Linux platforms
Expand Down
6 changes: 5 additions & 1 deletion src/common/config/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ enum ConfigKey
KEY_USE_FILESYSTEM_CACHE,
KEY_INLINE_SORT_THRESHOLD,
KEY_TEMP_PAGESPACE_DIR,
KEY_LEGACY_KERNEL_NAMES,
MAX_CONFIG_KEY // keep it last
};

Expand Down Expand Up @@ -306,7 +307,8 @@ constexpr ConfigEntry entries[MAX_CONFIG_KEY] =
{TYPE_STRING, "DataTypeCompatibility", false, nullptr},
{TYPE_BOOLEAN, "UseFileSystemCache", false, true},
{TYPE_INTEGER, "InlineSortThreshold", false, 1000}, // bytes
{TYPE_STRING, "TempTableDirectory", false, ""}
{TYPE_STRING, "TempTableDirectory", false, ""},
{TYPE_BOOLEAN, "UseLegacyKernelObjectsNames", true, false}
};


Expand Down Expand Up @@ -634,6 +636,8 @@ class Config : public RefCounted, public GlobalStorage
CONFIG_GET_PER_DB_KEY(ULONG, getInlineSortThreshold, KEY_INLINE_SORT_THRESHOLD, getInt);

CONFIG_GET_PER_DB_STR(getTempPageSpaceDirectory, KEY_TEMP_PAGESPACE_DIR);

CONFIG_GET_GLOBAL_BOOL(getLegacyKernelNames, KEY_LEGACY_KERNEL_NAMES);
};

// Implementation of interface to access master configuration file
Expand Down
3 changes: 3 additions & 0 deletions src/common/file_params.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ static const char* const SNAPSHOTS_FILE = "fb_snap_%s";

static const char* const TRACE_FILE = "fb" COMMON_FILE_PREFIX "_trace";
static const char* const USER_MAP_FILE = "fb" COMMON_FILE_PREFIX "_user_mapping";
static const char* const SHARED_EVENT = "fb" COMMON_FILE_PREFIX "_process%u_signal%d";
static const char* const SHARED_EVENT_OLD = "_firebird_process%u_signal%d";

static const char* const FB_TRACE_LOG_MUTEX = "fb_trace_log_mutex";

#ifdef UNIX
Expand Down
2 changes: 1 addition & 1 deletion src/common/isc_sync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2693,7 +2693,7 @@ static bool make_object_name(TEXT* buffer, size_t bufsize,

// CVC: I'm not convinced that if this call has no space to put the prefix,
// we can ignore that fact, hence I changed that signature, too.
if (!fb_utils::prefix_kernel_object_name(buffer, bufsize))
if (!fb_utils::private_kernel_object_name(buffer, bufsize))
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return false;
Expand Down
8 changes: 6 additions & 2 deletions src/common/os/win32/isc_ipc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
#include "../common/isc_proto.h"
#include "../common/os/isc_i_proto.h"
#include "../common/isc_s_proto.h"
#include "../common/file_params.h"
#include "../common/config/config.h"

#include <windows.h>
#include <process.h>
Expand Down Expand Up @@ -198,9 +200,11 @@ HANDLE ISC_make_signal(bool /*create_flag*/, bool manual_reset, int process_idL,
return CreateEvent(NULL, man_rst, FALSE, NULL);

TEXT event_name[BUFFER_TINY];
sprintf(event_name, "_firebird_process%u_signal%d", process_idL, signal_number);

if (!fb_utils::prefix_kernel_object_name(event_name, sizeof(event_name)))
const bool legacyNames = Firebird::Config::getLegacyKernelNames();
sprintf(event_name, legacyNames ? SHARED_EVENT_OLD : SHARED_EVENT, process_idL, signal_number);

if (!fb_utils::private_kernel_object_name(event_name, sizeof(event_name)))
{
SetLastError(ERROR_FILENAME_EXCED_RANGE);
return NULL;
Expand Down
215 changes: 207 additions & 8 deletions src/common/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@

#include "../common/gdsassert.h"
#include "../common/utils_proto.h"
#include "../common/classes/auto.h"
#include "../common/classes/locks.h"
#include "../common/classes/init.h"
#include "../common/isc_proto.h"
#include "../jrd/constants.h"
#include "firebird/impl/inf_pub.h"
#include "../jrd/align.h"
Expand All @@ -56,10 +58,12 @@
#include "../common/classes/ClumpletReader.h"
#include "../common/StatusArg.h"
#include "../common/TimeZoneUtil.h"
#include "../common/config/config.h"

#ifdef WIN_NT
#include <direct.h>
#include <io.h> // isatty()
#include <sddl.h>
#endif

#ifdef HAVE_UNISTD_H
Expand Down Expand Up @@ -438,26 +442,31 @@ bool prefix_kernel_object_name(char* name, size_t bufsize)
class DynLibHandle
{
public:
explicit DynLibHandle(HMODULE mod)
: m_handle(mod)
explicit DynLibHandle(HMODULE mod, bool unload = true)
: m_handle(mod), m_unload(unload)
{}

~DynLibHandle()
{
if (m_handle)
if (m_handle && m_unload)
FreeLibrary(m_handle);
}

operator HMODULE() const
{
return m_handle;
}
/* The previous conversion is invoked with !object so this is enough.
bool operator!() const

template <typename PFn>
bool getProcAddress(const char* name, PFn& addr) const
{
return !m_handle;
addr = (PFn) GetProcAddress(m_handle, name);
return (addr != nullptr);
}
*/

private:
HMODULE m_handle;
const HMODULE m_handle;
const bool m_unload;
};


Expand Down Expand Up @@ -555,6 +564,196 @@ bool isGlobalKernelPrefix()
}


// Incapsulates Windows private namespace
class PrivateNamespace
{
public:
PrivateNamespace(MemoryPool& pool) :
m_hNamespace(NULL),
m_hTestEvent(NULL)
{
try
{
init();
}
catch (const Firebird::Exception& ex)
{
iscLogException("Error creating private namespace", ex);
}
}

~PrivateNamespace()
{
if (m_hNamespace != NULL)
(*pClosePrivateNamespace)(m_hNamespace, 0);
if (m_hTestEvent != NULL)
CloseHandle(m_hTestEvent);
}

// Add namespace prefix to the name, returns true on success.
bool addPrefix(char* name, size_t bufsize)
{
if (!isReady())
return false;

if (strchr(name, '\\') != 0)
return false;

const size_t prefixLen = strlen(sPrivateNameSpace) + 1;
const size_t nameLen = strlen(name) + 1;
if (prefixLen + nameLen > bufsize)
return false;

memmove(name + prefixLen, name, nameLen + 1);
memcpy(name, sPrivateNameSpace, prefixLen - 1);
name[prefixLen - 1] = '\\';
return true;
}

bool isReady() const
{
return (m_hNamespace != NULL) || (m_hTestEvent != NULL);
}

private:
typedef HANDLE (APIENTRY *PFnCreateBoundaryDescriptorA)(LPCSTR Name, ULONG Flags);
typedef BOOL (WINAPI *PFnAddSIDToBoundaryDescriptor)(HANDLE* BoundaryDescriptor, PSID RequiredSid);
typedef VOID (WINAPI *PFnDeleteBoundaryDescriptor)(HANDLE BoundaryDescriptor);
typedef HANDLE (WINAPI *PFnCreatePrivateNamespaceA)(LPSECURITY_ATTRIBUTES lpPrivateNamespaceAttributes,
LPVOID lpBoundaryDescriptor, LPCSTR lpAliasPrefix);
typedef HANDLE (WINAPI *PFnOpenPrivateNamespaceA)(LPVOID lpBoundaryDescriptor, LPCSTR lpAliasPrefix);
typedef BOOLEAN (WINAPI *PFnClosePrivateNamespace)(HANDLE Handle, ULONG Flags);

PFnCreateBoundaryDescriptorA pCreateBoundaryDescriptor;
PFnAddSIDToBoundaryDescriptor pAddSIDToBoundaryDescriptor;
PFnDeleteBoundaryDescriptor pDeleteBoundaryDescriptor;
PFnCreatePrivateNamespaceA pCreatePrivateNamespace;
PFnOpenPrivateNamespaceA pOpenPrivateNamespace;
PFnClosePrivateNamespace pClosePrivateNamespace;


const char* sPrivateNameSpace = "FirebirdCommon";
const char* sBoundaryName = "FirebirdCommonBoundary";

void raiseError(const char* apiRoutine)
{
(Firebird::Arg::Gds(isc_sys_request) << apiRoutine << Firebird::Arg::OsError()).raise();
}

bool initEntrypoints()
{
DynLibHandle hKernel32(GetModuleHandle("kernel32.dll"), false);
if (!hKernel32)
return false;

return hKernel32.getProcAddress("CreateBoundaryDescriptorA", pCreateBoundaryDescriptor) &&
hKernel32.getProcAddress("AddSIDToBoundaryDescriptor", pAddSIDToBoundaryDescriptor) &&
hKernel32.getProcAddress("DeleteBoundaryDescriptor", pDeleteBoundaryDescriptor) &&
hKernel32.getProcAddress("CreatePrivateNamespaceA", pCreatePrivateNamespace) &&
hKernel32.getProcAddress("OpenPrivateNamespaceA", pOpenPrivateNamespace) &&
hKernel32.getProcAddress("ClosePrivateNamespace", pClosePrivateNamespace);
}

void init()
{
if (!initEntrypoints())
return;

alignas(SID) char sid[SECURITY_MAX_SID_SIZE];
DWORD cbSid = sizeof(sid);

// For now use EVERYONE, could be changed later
cbSid = sizeof(sid);
if (!CreateWellKnownSid(WinWorldSid, NULL, &sid, &cbSid))
raiseError("CreateWellKnownSid");

// Create security descriptor which allows generic access to the just created SID

SECURITY_ATTRIBUTES sa;
RtlSecureZeroMemory(&sa, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;

char strSecDesc[255];
LPSTR strSid = NULL;
if (ConvertSidToStringSid(&sid, &strSid))
{
snprintf(strSecDesc, sizeof(strSecDesc), "D:(A;;GA;;;%s)", strSid);
LocalFree(strSid);
}
else
strncpy(strSecDesc, "D:(A;;GA;;;WD)", sizeof(strSecDesc));

if (!ConvertStringSecurityDescriptorToSecurityDescriptor(strSecDesc, SDDL_REVISION_1,
&sa.lpSecurityDescriptor, NULL))
{
raiseError("ConvertStringSecurityDescriptorToSecurityDescriptor");
}

Firebird::Cleanup cleanSecDesc( [&sa] {
LocalFree(sa.lpSecurityDescriptor);
});

HANDLE hBoundaryDesc = (*pCreateBoundaryDescriptor)(sBoundaryName, 0);
if (hBoundaryDesc == NULL)
raiseError("CreateBoundaryDescriptor");

Firebird::Cleanup cleanBndDesc( [this, &hBoundaryDesc] {
(*pDeleteBoundaryDescriptor)(hBoundaryDesc);
});

if (!(*pAddSIDToBoundaryDescriptor)(&hBoundaryDesc, &sid))
raiseError("AddSIDToBoundaryDescriptor");

m_hNamespace = (*pCreatePrivateNamespace)(&sa, hBoundaryDesc, sPrivateNameSpace);

if (m_hNamespace == NULL)
{
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS)
raiseError("CreatePrivateNamespace");

m_hNamespace = (*pOpenPrivateNamespace)(hBoundaryDesc, sPrivateNameSpace);
if (m_hNamespace == NULL)
{
err = GetLastError();
if (err != ERROR_DUP_NAME)
raiseError("OpenPrivateNamespace");

Firebird::string name(sPrivateNameSpace);
name.append("\\test");

m_hTestEvent = CreateEvent(ISC_get_security_desc(), TRUE, TRUE, name.c_str());
if (m_hTestEvent == NULL)
raiseError("CreateEvent");
}
}
}

HANDLE m_hNamespace;
HANDLE m_hTestEvent;
};

static Firebird::InitInstance<PrivateNamespace> privateNamespace;


bool private_kernel_object_name(char* name, size_t bufsize)
{
const bool legacyNames = Firebird::Config::getLegacyKernelNames();

if (legacyNames || !privateNamespace().addPrefix(name, bufsize))
return prefix_kernel_object_name(name, bufsize);

return true;
}

bool privateNameSpaceReady()
{
const bool legacyNames = Firebird::Config::getLegacyKernelNames();
return !legacyNames && privateNamespace().isReady();
}


// This is a very basic registry querying class. Not much validation, but avoids
// leaving the registry open by mistake.

Expand Down
2 changes: 2 additions & 0 deletions src/common/utils_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ namespace fb_utils
#ifdef WIN_NT
bool prefix_kernel_object_name(char* name, size_t bufsize);
bool isGlobalKernelPrefix();
bool private_kernel_object_name(char* name, size_t bufsize);
bool privateNameSpaceReady();
#endif

// Compare the absolute value of two SINT64 numbers.
Expand Down

0 comments on commit 6cb2297

Please sign in to comment.