From 8d98b5d8df206e40c68ce958ca6be8a93e136455 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 10 Mar 2026 13:38:06 +0300 Subject: [PATCH 1/4] Remove security class and user privileges from the domain being dropped (see #8881: Large amount of unnecessary privileges in RDB for SYSDBA) --- src/dsql/DdlNodes.epp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 02e4550a85c..95daa293054 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -10894,6 +10894,12 @@ void DropRelationNode::deleteGlobalField(thread_db* tdbb, jrd_tra* transaction, ARG.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME) { DropDomainNode::deleteDimensionRecords(tdbb, transaction, globalName); + + if (!FLD.RDB$SECURITY_CLASS.NULL) + deleteSecurityClass(tdbb, transaction, FLD.RDB$SECURITY_CLASS); + + deletePrivilegesByRelName(tdbb, transaction, globalName, obj_field); + ERASE FLD; } END_FOR From eb420538ccd524e14f32304ed6b74ac60423a32b Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 10 Mar 2026 18:02:05 +0300 Subject: [PATCH 2/4] Fix TDBB_dont_post_dfw usage for procedures/functions. Don't grant USAGE permissions (and don't create a security class) for implicit domains (see #8881: Large amount of unnecessary privileges in RDB for SYSDBA). --- src/dsql/DdlNodes.epp | 110 +++++++++++++++++++++++------------------- src/dsql/DdlNodes.h | 8 +-- src/jrd/tdbb.h | 1 + src/jrd/vio.cpp | 3 ++ 4 files changed, 69 insertions(+), 53 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 95daa293054..3d3840c044a 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1111,8 +1111,16 @@ void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, QualifiedN Arg::Gds(isc_dsql_max_arr_dim_exceeded)); } + bool implicitDomain = false; + if (name.object.isEmpty()) + { DYN_UTIL_generate_field_name(tdbb, name); + implicitDomain = true; + } + + // Avoid the security class being assigned for implicit domains + AutoSetRestoreFlag noSecurityClass(&tdbb->tdbb_flags, implicitDomain ? TDBB_no_security_class : 0, true); AutoCacheRequest requestHandle(tdbb, drq_s_fld_src, DYN_REQUESTS); @@ -1198,7 +1206,8 @@ void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, QualifiedN } } - storePrivileges(tdbb, transaction, name, obj_field, USAGE_PRIVILEGES); + if (!implicitDomain) + storePrivileges(tdbb, transaction, name, obj_field, USAGE_PRIVILEGES); } @@ -1893,14 +1902,14 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql // first pass if (alterIndividualParameters) { - if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true)) + if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, false, true)) altered = true; else status_exception::raise(Arg::Gds(isc_dyn_func_not_found) << name.toQuotedString()); } else if (alter) { - if (executeAlter(tdbb, dsqlScratch, transaction, false, true)) + if (executeAlter(tdbb, dsqlScratch, transaction, false, false, true)) altered = true; else { @@ -1915,26 +1924,22 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql fb_assert(id); auto* permanent = MetadataCache::newVersion(tdbb, id); + try { auto* prc = permanent ? permanent->getVersioned(tdbb, CacheFlag::NOCOMMIT | CacheFlag::MINISCAN) : nullptr; { bool dummy = false; - AutoSetRestore compiling(prc ? &(prc->compiling) : &dummy, true); + AutoSetRestore compiling(prc ? &prc->compiling : &dummy, true); compile(tdbb, dsqlScratch); } - { // scope - // avoid modify routine dfw during second pass on CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, altered ? 0 : TDBB_dont_post_dfw, true); - - // second pass - if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); - else - executeAlter(tdbb, dsqlScratch, transaction, true, false); - } + // second pass + if (alterIndividualParameters) + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, !altered, true, false); + else + executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); if (name.package.isEmpty()) { @@ -2034,18 +2039,15 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch if (name.package.isEmpty()) storePrivileges(tdbb, transaction, name, obj_udf, EXEC_PRIVILEGES); - // avoid modify routine dfw when execute CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true); - - executeAlter(tdbb, dsqlScratch, transaction, false, false); + executeAlter(tdbb, dsqlScratch, transaction, true, false, false); return true; } bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) { - Attachment* const attachment = transaction->getAttachment(); + const auto attachment = transaction->getAttachment(); bool modified = false; unsigned returnPos = 0; @@ -2075,6 +2077,9 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER); } + // Avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); + MODIFY FUN if (secondPass) { @@ -2235,14 +2240,16 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* if (!secondPass && modified) { - // Get all comments and defaults from the old parameter list. + // Get all comments and defaults from the old parameter list + CollectedParameterMap collectedParameters; collectParameters(tdbb, transaction, collectedParameters); - // delete all old arguments + // Delete all old arguments ... + DropFunctionNode::dropArguments(tdbb, transaction, name); - // and insert the new ones + // ... and insert the new ones if (returnType && returnType->type) storeArgument(tdbb, dsqlScratch, transaction, returnPos, true, returnType, NULL); @@ -2264,9 +2271,9 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* } bool CreateAlterFunctionNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) { - Attachment* const attachment = transaction->getAttachment(); + const auto attachment = transaction->getAttachment(); bool modified = false; @@ -2290,6 +2297,9 @@ bool CreateAlterFunctionNode::executeAlterIndividualParameters(thread_db* tdbb, id = FUN.RDB$FUNCTION_ID; + // Avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); + MODIFY FUN if (deterministic.isAssigned()) { @@ -2967,14 +2977,14 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq // first pass if (alterIndividualParameters) { - if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true)) + if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, false, true)) altered = true; else status_exception::raise(Arg::Gds(isc_dyn_proc_not_found) << name.toQuotedString()); } else if (alter) { - if (executeAlter(tdbb, dsqlScratch, transaction, false, true)) + if (executeAlter(tdbb, dsqlScratch, transaction, false, false, true)) altered = true; else { @@ -2989,26 +2999,22 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq fb_assert(id); auto* permanent = MetadataCache::newVersion(tdbb, id); + try { auto* prc = permanent ? permanent->getVersioned(tdbb, CacheFlag::NOCOMMIT | CacheFlag::MINISCAN) : nullptr; { bool dummy = false; - AutoSetRestore compiling(prc ? &(prc->compiling) : &dummy, true); + AutoSetRestore compiling(prc ? &prc->compiling : &dummy, true); compile(tdbb, dsqlScratch); } - { // scope - // avoid modify routine dfw during second pass on CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, altered ? 0 : TDBB_dont_post_dfw, true); - - // second pass - if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); - else - executeAlter(tdbb, dsqlScratch, transaction, true, false); - } + // second pass + if (alterIndividualParameters) + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, !altered, true, false); + else + executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); if (name.package.isEmpty()) { @@ -3101,18 +3107,16 @@ bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc if (name.package.isEmpty()) storePrivileges(tdbb, transaction, name, obj_procedure, EXEC_PRIVILEGES); - // avoid modify routine dfw when execute CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, TDBB_dont_post_dfw, true); - - executeAlter(tdbb, dsqlScratch, transaction, false, false); + executeAlter(tdbb, dsqlScratch, transaction, true, false, false); return true; } bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) { - Attachment* const attachment = transaction->getAttachment(); + const auto attachment = transaction->getAttachment(); + AutoCacheRequest requestHandle(tdbb, drq_m_prcs2, DYN_REQUESTS); bool modified = false; @@ -3141,6 +3145,9 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER); } + // Avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); + MODIFY P if (secondPass) { @@ -3227,14 +3234,16 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch if (!secondPass && modified) { - // Get all comments and defaults from the old parameter list. + // Get all comments and defaults from the old parameter list + CollectedParameterMap collectedParameters; collectParameters(tdbb, transaction, collectedParameters); - // Delete all old input and output parameters. + // Delete all old input and output parameters ... + DropProcedureNode::dropParameters(tdbb, transaction, name); - // And insert the new ones. + // ... and insert the new ones for (FB_SIZE_T i = 0; i < parameters.getCount(); ++i) { @@ -3312,9 +3321,9 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch } bool CreateAlterProcedureNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) { - Attachment* const attachment = transaction->getAttachment(); + const auto attachment = transaction->getAttachment(); bool modified = false; @@ -3338,6 +3347,9 @@ bool CreateAlterProcedureNode::executeAlterIndividualParameters(thread_db* tdbb, id = P.RDB$PROCEDURE_ID; + // Avoid modify routine dfw when execute CREATE + AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); + MODIFY P if (ssDefiner.has_value()) { diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index b72ac145e9a..1bf3cb7bb8f 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -442,9 +442,9 @@ class CreateAlterFunctionNode final : public DdlNode bool executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool secondPass, bool runTriggers); + bool create, bool secondPass, bool runTriggers); bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool secondPass, bool runTriggers); + bool create, bool secondPass, bool runTriggers); void storeArgument(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, unsigned pos, bool returnArg, ParameterClause* parameter, @@ -590,9 +590,9 @@ class CreateAlterProcedureNode final : public DdlNode private: bool executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool secondPass, bool runTriggers); + bool create, bool secondPass, bool runTriggers); bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool secondPass, bool runTriggers); + bool create, bool secondPass, bool runTriggers); void storeParameter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, USHORT parameterType, unsigned pos, ParameterClause* parameter, diff --git a/src/jrd/tdbb.h b/src/jrd/tdbb.h index 7d5406c8294..73d4dfd9b27 100644 --- a/src/jrd/tdbb.h +++ b/src/jrd/tdbb.h @@ -186,6 +186,7 @@ inline constexpr ULONG TDBB_dfw_cleanup = 4096; // DFW cleanup phase is activ inline constexpr ULONG TDBB_repl_in_progress = 8192; // Prevent recursion in replication inline constexpr ULONG TDBB_replicator = 16384; // Replicator inline constexpr ULONG TDBB_async = 32768; // Async context (set in AST) +inline constexpr ULONG TDBB_no_security_class = 65536; // don't assign a security class to the object being created class thread_db final : public Firebird::ThreadData { diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 4a20d727a11..708becaf320 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -7183,6 +7183,9 @@ static bool set_security_class(thread_db* tdbb, Record* record, USHORT field_id) **************************************/ dsc desc1; + if (tdbb->tdbb_flags & TDBB_no_security_class) + return false; + if (!EVL_field(0, record, field_id, &desc1)) { const SINT64 value = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_sec_id, SQL_SECCLASS_GENERATOR); From d6056d6ae3e3952dafa88577877fc5d42f2653e4 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 10 Mar 2026 19:51:58 +0300 Subject: [PATCH 3/4] Correction after Alex's review --- src/dsql/DdlNodes.epp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 3d3840c044a..e154a877041 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1937,7 +1937,7 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql // second pass if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, !altered, true, false); + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true, false); else executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); @@ -3012,7 +3012,7 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq // second pass if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, !altered, true, false); + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true, false); else executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); From 5e4283aa8047bf96a450f564ff5d86f7fc0c3785 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 10 Mar 2026 21:04:20 +0300 Subject: [PATCH 4/4] Cleanup, thanks to Alex --- src/dsql/DdlNodes.epp | 18 ++++++------------ src/dsql/DdlNodes.h | 4 ++-- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index e154a877041..3648df1305c 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1902,7 +1902,7 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql // first pass if (alterIndividualParameters) { - if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, false, true)) + if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true)) altered = true; else status_exception::raise(Arg::Gds(isc_dyn_func_not_found) << name.toQuotedString()); @@ -1937,7 +1937,7 @@ void CreateAlterFunctionNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsql // second pass if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true, false); + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); else executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); @@ -2271,7 +2271,7 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* } bool CreateAlterFunctionNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool secondPass, bool runTriggers) { const auto attachment = transaction->getAttachment(); @@ -2297,9 +2297,6 @@ bool CreateAlterFunctionNode::executeAlterIndividualParameters(thread_db* tdbb, id = FUN.RDB$FUNCTION_ID; - // Avoid modify routine dfw when execute CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); - MODIFY FUN if (deterministic.isAssigned()) { @@ -2977,7 +2974,7 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq // first pass if (alterIndividualParameters) { - if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, false, true)) + if (executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true)) altered = true; else status_exception::raise(Arg::Gds(isc_dyn_proc_not_found) << name.toQuotedString()); @@ -3012,7 +3009,7 @@ void CreateAlterProcedureNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsq // second pass if (alterIndividualParameters) - executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, false, true, false); + executeAlterIndividualParameters(tdbb, dsqlScratch, transaction, true, false); else executeAlter(tdbb, dsqlScratch, transaction, !altered, true, false); @@ -3321,7 +3318,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch } bool CreateAlterProcedureNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, bool create, bool secondPass, bool runTriggers) + jrd_tra* transaction, bool secondPass, bool runTriggers) { const auto attachment = transaction->getAttachment(); @@ -3347,9 +3344,6 @@ bool CreateAlterProcedureNode::executeAlterIndividualParameters(thread_db* tdbb, id = P.RDB$PROCEDURE_ID; - // Avoid modify routine dfw when execute CREATE - AutoSetRestoreFlag noDfw(&tdbb->tdbb_flags, create ? TDBB_dont_post_dfw : 0, true); - MODIFY P if (ssDefiner.has_value()) { diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 1bf3cb7bb8f..389a04abe3d 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -444,7 +444,7 @@ class CreateAlterFunctionNode final : public DdlNode bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, bool create, bool secondPass, bool runTriggers); bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool create, bool secondPass, bool runTriggers); + bool secondPass, bool runTriggers); void storeArgument(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, unsigned pos, bool returnArg, ParameterClause* parameter, @@ -592,7 +592,7 @@ class CreateAlterProcedureNode final : public DdlNode bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, bool create, bool secondPass, bool runTriggers); bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, - bool create, bool secondPass, bool runTriggers); + bool secondPass, bool runTriggers); void storeParameter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, USHORT parameterType, unsigned pos, ParameterClause* parameter,