import Oracle_OSS 389-ds-base-1.4.3.39-24.module+el8.10.0+90914+745232ac
This commit is contained in:
parent
1d52b251a9
commit
c4763fe41f
@ -0,0 +1,399 @@
|
||||
From 71583a67249df813098f0cca39b04198b76e6349 Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Mon, 30 Mar 2026 22:18:49 -0700
|
||||
Subject: [PATCH 01/13] Issue 7366 - Memory leaks in syncrepl plugin during
|
||||
persistent search operations (#7367)
|
||||
|
||||
Description: When running a syncrepl persistent search as a non-root user,
|
||||
LeakSanitizer detects memory leaks in the sync repl plugin.
|
||||
Multiple cleanup paths for SyncRequest had inconsistent
|
||||
and incomplete resource freeing, leaking pblock contents, filters,
|
||||
queue nodes, and base DNs. Per-thread operation lists were also leaked
|
||||
on thread exit due to a missing NSPR thread-private destructor.
|
||||
|
||||
Consolidate all SyncRequest cleanup into sync_request_free() and
|
||||
register a destructor for per-thread OPERATION_PL_CTX_T lists.
|
||||
|
||||
Add a test for non-root persistent syncrepl search verifying no
|
||||
OPERATIONS_ERROR or 'Missing aclpb' errors.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7366
|
||||
|
||||
Reviewed by: @progier389 (Thanks!)
|
||||
---
|
||||
.../suites/syncrepl_plugin/basic_test.py | 176 +++++++++++++++++-
|
||||
ldap/servers/plugins/sync/sync_init.c | 24 ++-
|
||||
ldap/servers/plugins/sync/sync_persist.c | 103 ++++++----
|
||||
3 files changed, 264 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
index cdf35eeaa..ff9be816f 100644
|
||||
--- a/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
+++ b/dirsrvtests/tests/suites/syncrepl_plugin/basic_test.py
|
||||
@@ -20,7 +20,7 @@ from lib389.idm.group import Groups
|
||||
from lib389.topologies import topology_st as topology
|
||||
from lib389.topologies import topology_m2 as topo_m2
|
||||
from lib389.paths import Paths
|
||||
-from lib389.utils import ds_is_older
|
||||
+from lib389.utils import ds_is_older, is_fips, ensure_bytes
|
||||
from lib389.plugins import RetroChangelogPlugin, ContentSyncPlugin, ContentSynchronizationPlugin, AutoMembershipPlugin, MemberOfPlugin, MemberOfSharedConfig, AutoMembershipDefinitions, MEPTemplates, MEPConfigs, ManagedEntriesPlugin, MEPTemplate
|
||||
from lib389._constants import *
|
||||
|
||||
@@ -735,3 +735,177 @@ def test_sync_repl_invalid_cookie(topology, request):
|
||||
pass
|
||||
|
||||
request.addfinalizer(fin)
|
||||
+
|
||||
+
|
||||
+def test_sync_repl_non_root_persist(topology, request):
|
||||
+ """Test sync_repl persistent search as a non-root user triggers
|
||||
+ proper ACL evaluation without causing OPERATIONS_ERROR.
|
||||
+
|
||||
+ :id: 97f4be3d-f8c3-45cf-8295-ed58ee7bd371
|
||||
+ :setup: Standalone Instance
|
||||
+ :steps:
|
||||
+ 1. Enable RetroChangelog and ContentSync plugins
|
||||
+ 2. Create a test user and set its password
|
||||
+ 3. Add an ACI granting the test user read access
|
||||
+ 4. Run a syncrepl persistent search bound as the test user
|
||||
+ 5. Add entries while the persistent search is active
|
||||
+ 6. Stop the server to end the sync thread
|
||||
+ 7. Check that the sync client received results without error
|
||||
+ 8. Check the error log for 'Missing aclpb' messages
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ 4. Success - no OPERATIONS_ERROR
|
||||
+ 5. Success
|
||||
+ 6. Success
|
||||
+ 7. The sync client should have received cookies
|
||||
+ 8. No 'Missing aclpb' errors in the log
|
||||
+ """
|
||||
+ inst = topology.standalone
|
||||
+ inst.restart()
|
||||
+ inst.enable_tls()
|
||||
+
|
||||
+ # Enable RetroChangelog
|
||||
+ rcl = RetroChangelogPlugin(inst)
|
||||
+ rcl.disable()
|
||||
+ rcl.enable()
|
||||
+ rcl.set('nsslapd-attribute', 'nsuniqueid:targetUniqueId')
|
||||
+
|
||||
+ # Enable Content Sync plugin
|
||||
+ csp = ContentSyncPlugin(inst)
|
||||
+ csp.enable()
|
||||
+
|
||||
+ inst.restart()
|
||||
+
|
||||
+ # Create the syncrepl user
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ sync_user = users.create_test_user(uid=29999)
|
||||
+ sync_user.set('userPassword', 'sync_password')
|
||||
+ SYNC_USER_DN = sync_user.dn
|
||||
+
|
||||
+ # Add ACI granting the sync user read access to the suffix
|
||||
+ ACI = (
|
||||
+ '(targetattr="*")(version 3.0; acl "Sync user read access";'
|
||||
+ ' allow (read, search, compare)'
|
||||
+ ' userdn = "ldap:///%s";)' % SYNC_USER_DN
|
||||
+ )
|
||||
+ inst.modify_s(DEFAULT_SUFFIX,
|
||||
+ [(ldap.MOD_ADD, 'aci', ensure_bytes(ACI))])
|
||||
+
|
||||
+ # Start a syncrepl persistent search as the non-root user
|
||||
+ # Using the Sync_persist-like pattern but with non-root bind
|
||||
+ sync_error = [None] # mutable container to capture errors from thread
|
||||
+ sync_cookies = []
|
||||
+
|
||||
+ class NonRootSyncer(ReconnectLDAPObject, SyncreplConsumer):
|
||||
+ def __init__(self, *args, **kwargs):
|
||||
+ self.cookie = None
|
||||
+ self.cookies = []
|
||||
+ ldap.ldapobject.ReconnectLDAPObject.__init__(self, *args, **kwargs)
|
||||
+
|
||||
+ def syncrepl_set_cookie(self, cookie):
|
||||
+ self.cookie = cookie
|
||||
+ self.cookies.append(cookie.split('#')[2])
|
||||
+
|
||||
+ def syncrepl_get_cookie(self):
|
||||
+ return self.cookie
|
||||
+
|
||||
+ def syncrepl_present(self, uuids, refreshDeletes=False):
|
||||
+ pass
|
||||
+
|
||||
+ def syncrepl_delete(self, uuids):
|
||||
+ pass
|
||||
+
|
||||
+ def syncrepl_entry(self, dn, attrs, uuid):
|
||||
+ log.info("NonRootSyncer received entry: %s" % dn)
|
||||
+
|
||||
+ def syncrepl_refreshdone(self):
|
||||
+ log.info("NonRootSyncer refresh done")
|
||||
+
|
||||
+ def get_cookies(self):
|
||||
+ return self.cookies
|
||||
+
|
||||
+ def sync_thread_func():
|
||||
+ ldap_conn = None
|
||||
+ try:
|
||||
+ ldap_conn = NonRootSyncer(inst.toLDAPURL())
|
||||
+ ldap_conn.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND)
|
||||
+ ldap_conn.set_option(ldap.OPT_X_TLS_CACERTFILE,
|
||||
+ os.path.join(inst.get_config_dir(), "ca.crt"))
|
||||
+ if is_fips():
|
||||
+ ldap_conn.set_option(ldap.OPT_X_TLS_PROTOCOL_MIN,
|
||||
+ ldap.OPT_X_TLS_PROTOCOL_TLS1_2)
|
||||
+ ldap_conn.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
|
||||
+
|
||||
+ # Bind as the non-root user
|
||||
+ ldap_conn.simple_bind_s(SYNC_USER_DN, 'sync_password')
|
||||
+
|
||||
+ ldap_search = ldap_conn.syncrepl_search(
|
||||
+ DEFAULT_SUFFIX,
|
||||
+ ldap.SCOPE_SUBTREE,
|
||||
+ mode='refreshAndPersist',
|
||||
+ attrlist=['objectclass', 'cn', 'uid', 'sn'],
|
||||
+ filterstr='(objectClass=person)',
|
||||
+ cookie=None
|
||||
+ )
|
||||
+
|
||||
+ while ldap_conn.syncrepl_poll(all=1, msgid=ldap_search):
|
||||
+ pass
|
||||
+ except ldap.OPERATIONS_ERROR as e:
|
||||
+ log.error("SyncRepl got OPERATIONS_ERROR: %s" % e)
|
||||
+ sync_error[0] = e
|
||||
+ except (ldap.SERVER_DOWN, ldap.CONNECT_ERROR):
|
||||
+ pass
|
||||
+ finally:
|
||||
+ if ldap_conn:
|
||||
+ sync_cookies.extend(ldap_conn.get_cookies())
|
||||
+
|
||||
+ t = threading.Thread(target=sync_thread_func)
|
||||
+ t.daemon = True
|
||||
+ t.start()
|
||||
+ time.sleep(5)
|
||||
+
|
||||
+ # Add entries while persistent search is active (as directory manager)
|
||||
+ test_users = []
|
||||
+ for i in range(20001, 20004):
|
||||
+ test_users.append(users.create_test_user(uid=i))
|
||||
+ time.sleep(5)
|
||||
+
|
||||
+ # Stop the server to end the sync thread cleanly
|
||||
+ inst.stop()
|
||||
+ t.join(timeout=30)
|
||||
+
|
||||
+ # Verify no OPERATIONS_ERROR was raised
|
||||
+ assert sync_error[0] is None, \
|
||||
+ "SyncRepl persistent search as non-root user got OPERATIONS_ERROR: %s" % sync_error[0]
|
||||
+
|
||||
+ # Verify the sync client received some cookies (entries were synced)
|
||||
+ assert len(sync_cookies) > 0, \
|
||||
+ "SyncRepl persistent search as non-root user received no cookies"
|
||||
+
|
||||
+ # Restart and check error log for the specific aclpb error
|
||||
+ inst.start()
|
||||
+ assert not inst.searchErrorsLog('Missing aclpb'), \
|
||||
+ "Found 'Missing aclpb' errors in the error log"
|
||||
+
|
||||
+ log.info('test_sync_repl_non_root_persist: PASS')
|
||||
+
|
||||
+ def fin():
|
||||
+ inst.restart()
|
||||
+ try:
|
||||
+ inst.modify_s(DEFAULT_SUFFIX,
|
||||
+ [(ldap.MOD_DELETE, 'aci', ensure_bytes(ACI))])
|
||||
+ except:
|
||||
+ pass
|
||||
+ for user in test_users:
|
||||
+ try:
|
||||
+ user.delete()
|
||||
+ except:
|
||||
+ pass
|
||||
+ try:
|
||||
+ sync_user.delete()
|
||||
+ except:
|
||||
+ pass
|
||||
+
|
||||
+ request.addfinalizer(fin)
|
||||
diff --git a/ldap/servers/plugins/sync/sync_init.c b/ldap/servers/plugins/sync/sync_init.c
|
||||
index 49c7387bf..28c0edf06 100644
|
||||
--- a/ldap/servers/plugins/sync/sync_init.c
|
||||
+++ b/ldap/servers/plugins/sync/sync_init.c
|
||||
@@ -20,6 +20,28 @@ static int sync_persist_register_operation_extension(void);
|
||||
|
||||
static PRUintn thread_primary_op;
|
||||
|
||||
+/*
|
||||
+ * Destructor for the per-thread primary operation list HEAD node.
|
||||
+ * Called by NSPR when a thread exits, to free the HEAD node
|
||||
+ * and any remaining linked list entries.
|
||||
+ */
|
||||
+static void
|
||||
+sync_thread_primary_op_destructor(void *priv)
|
||||
+{
|
||||
+ OPERATION_PL_CTX_T *head = (OPERATION_PL_CTX_T *)priv;
|
||||
+ if (head) {
|
||||
+ OPERATION_PL_CTX_T *curr_op = head->next;
|
||||
+ while (curr_op) {
|
||||
+ OPERATION_PL_CTX_T *next = curr_op->next;
|
||||
+ slapi_entry_free(curr_op->entry);
|
||||
+ slapi_entry_free(curr_op->eprev);
|
||||
+ slapi_ch_free((void **)&curr_op);
|
||||
+ curr_op = next;
|
||||
+ }
|
||||
+ slapi_ch_free((void **)&head);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
int
|
||||
sync_init(Slapi_PBlock *pb)
|
||||
{
|
||||
@@ -187,7 +209,7 @@ sync_start(Slapi_PBlock *pb)
|
||||
* only contains one operation. For nested, the list contains the operations
|
||||
* in the order that they were applied
|
||||
*/
|
||||
- PR_NewThreadPrivateIndex(&thread_primary_op, NULL);
|
||||
+ PR_NewThreadPrivateIndex(&thread_primary_op, sync_thread_primary_op_destructor);
|
||||
sync_persist_initialize(argc, argv);
|
||||
|
||||
return (0);
|
||||
diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c
|
||||
index 283607361..a78c273cd 100644
|
||||
--- a/ldap/servers/plugins/sync/sync_persist.c
|
||||
+++ b/ldap/servers/plugins/sync/sync_persist.c
|
||||
@@ -43,6 +43,69 @@ static void sync_node_free(SyncQueueNode **node);
|
||||
static int sync_acquire_connection(Slapi_Connection *conn);
|
||||
static int sync_release_connection(Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operation *op, int release);
|
||||
|
||||
+/*
|
||||
+ * Free all resources owned by a SyncRequest and the request itself.
|
||||
+ * Caller must remove the request from the list (sync_remove_request)
|
||||
+ * before calling this, if it was added.
|
||||
+ */
|
||||
+static void
|
||||
+sync_request_free(SyncRequest **reqp)
|
||||
+{
|
||||
+ SyncRequest *req;
|
||||
+ SyncQueueNode *qnode, *qnodenext;
|
||||
+
|
||||
+ if (reqp == NULL || *reqp == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+ req = *reqp;
|
||||
+
|
||||
+ if (req->req_pblock) {
|
||||
+ char **attrs_dup = NULL;
|
||||
+ char *strFilter = NULL;
|
||||
+ LDAPControl **ctrls = NULL;
|
||||
+ Slapi_DN *sdn = NULL;
|
||||
+
|
||||
+ slapi_pblock_get(req->req_pblock, SLAPI_SEARCH_TARGET_SDN, &sdn);
|
||||
+ slapi_sdn_free(&sdn);
|
||||
+ slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_TARGET_SDN, NULL);
|
||||
+
|
||||
+ slapi_pblock_get(req->req_pblock, SLAPI_SEARCH_ATTRS, &attrs_dup);
|
||||
+ slapi_ch_array_free(attrs_dup);
|
||||
+ slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_ATTRS, NULL);
|
||||
+
|
||||
+ slapi_pblock_get(req->req_pblock, SLAPI_SEARCH_STRFILTER, &strFilter);
|
||||
+ slapi_ch_free((void **)&strFilter);
|
||||
+ slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_STRFILTER, NULL);
|
||||
+
|
||||
+ slapi_pblock_get(req->req_pblock, SLAPI_REQCONTROLS, &ctrls);
|
||||
+ if (ctrls) {
|
||||
+ ldap_controls_free(ctrls);
|
||||
+ slapi_pblock_set(req->req_pblock, SLAPI_REQCONTROLS, NULL);
|
||||
+ }
|
||||
+
|
||||
+ slapi_pblock_destroy(req->req_pblock);
|
||||
+ req->req_pblock = NULL;
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free((void **)&req->req_orig_base);
|
||||
+ slapi_filter_free(req->req_filter, 1);
|
||||
+ req->req_filter = NULL;
|
||||
+
|
||||
+ for (qnode = req->ps_eq_head; qnode; qnode = qnodenext) {
|
||||
+ qnodenext = qnode->sync_next;
|
||||
+ sync_node_free(&qnode);
|
||||
+ }
|
||||
+ req->ps_eq_head = NULL;
|
||||
+ req->ps_eq_tail = NULL;
|
||||
+
|
||||
+ if (req->req_lock) {
|
||||
+ PR_DestroyLock(req->req_lock);
|
||||
+ req->req_lock = NULL;
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free((void **)reqp);
|
||||
+}
|
||||
+
|
||||
/* This routine appends the operation at the end of the
|
||||
* per thread pending list of nested operation..
|
||||
* being a betxn_preop the pending list has the same order
|
||||
@@ -664,10 +727,7 @@ sync_persist_add(Slapi_PBlock *pb)
|
||||
prerr, slapi_pr_strerror(prerr));
|
||||
/* Now remove the ps from the list so call the function ps_remove */
|
||||
sync_remove_request(req);
|
||||
- PR_DestroyLock(req->req_lock);
|
||||
- req->req_lock = NULL;
|
||||
- slapi_ch_free((void **)&req->req_pblock);
|
||||
- slapi_ch_free((void **)&req);
|
||||
+ sync_request_free(&req);
|
||||
} else {
|
||||
thread_count++;
|
||||
return (req->req_tid);
|
||||
@@ -753,11 +813,7 @@ sync_persist_terminate_all()
|
||||
/* it frees the structures, just in case it remained connected sync_repl client */
|
||||
for (req = sync_request_list->sync_req_head; NULL != req; req = next) {
|
||||
next = req->req_next;
|
||||
- slapi_pblock_destroy(req->req_pblock);
|
||||
- req->req_pblock = NULL;
|
||||
- PR_DestroyLock(req->req_lock);
|
||||
- req->req_lock = NULL;
|
||||
- slapi_ch_free((void **)&req);
|
||||
+ sync_request_free(&req);
|
||||
}
|
||||
slapi_ch_free((void **)&sync_request_list);
|
||||
}
|
||||
@@ -1060,34 +1116,7 @@ sync_send_results(void *arg)
|
||||
done:
|
||||
/* This client closed the connection or shutdown, free the req */
|
||||
sync_remove_request(req);
|
||||
- PR_DestroyLock(req->req_lock);
|
||||
- req->req_lock = NULL;
|
||||
-
|
||||
- slapi_pblock_get(req->req_pblock, SLAPI_SEARCH_ATTRS, &attrs_dup);
|
||||
- slapi_ch_array_free(attrs_dup);
|
||||
- slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_ATTRS, NULL);
|
||||
-
|
||||
- slapi_pblock_get(req->req_pblock, SLAPI_SEARCH_STRFILTER, &strFilter);
|
||||
- slapi_ch_free((void **)&strFilter);
|
||||
- slapi_pblock_set(req->req_pblock, SLAPI_SEARCH_STRFILTER, NULL);
|
||||
-
|
||||
- slapi_pblock_get(req->req_pblock, SLAPI_REQCONTROLS, &ctrls);
|
||||
- if (ctrls) {
|
||||
- ldap_controls_free(ctrls);
|
||||
- slapi_pblock_set(req->req_pblock, SLAPI_REQCONTROLS, NULL);
|
||||
- }
|
||||
-
|
||||
- slapi_pblock_destroy(req->req_pblock);
|
||||
- req->req_pblock = NULL;
|
||||
-
|
||||
- slapi_ch_free((void **)&req->req_orig_base);
|
||||
- slapi_filter_free(req->req_filter, 1);
|
||||
- sync_cookie_free(&req->req_cookie);
|
||||
- for (qnode = req->ps_eq_head; qnode; qnode = qnodenext) {
|
||||
- qnodenext = qnode->sync_next;
|
||||
- sync_node_free(&qnode);
|
||||
- }
|
||||
- slapi_ch_free((void **)&req);
|
||||
+ sync_request_free(&req);
|
||||
thread_count--;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,197 @@
|
||||
From f02302eb0967647fc0bb08b9de9900f6c9b47396 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 2 Apr 2026 11:44:19 +0200
|
||||
Subject: [PATCH 02/13] Issue 7126 - WARN - keys2idl - received NULL idl from
|
||||
index_read_ext_allids (#7127)
|
||||
|
||||
Bug Description:
|
||||
Under high concurrency, occasional NULL IDL warnings were logged:
|
||||
|
||||
[29/Nov/2025:22:59:13.930441639 +0000] - WARN - keys2idl - received NULL idl from index_read_ext_allids, treating as empty set
|
||||
[29/Nov/2025:22:59:13.936543122 +0000] - WARN - keys2idl - this is probably a bug that should be reported
|
||||
[29/Nov/2025:22:59:13.947131944 +0000] - ERR - build_candidate_list - Database error -12795
|
||||
|
||||
Since #7124 all cursor operations use transaction isolation, which
|
||||
increases read-write lock contention under high concurrency. Deadlocked
|
||||
read transactions returned DBI_RC_RETRY, and after retry exhaustion
|
||||
`index_read_ext_allids()` returned NULL to callers that did not expect it.
|
||||
|
||||
Fix Description:
|
||||
* Ensure `index_read_ext_allids()` never returns NULL. All early exits
|
||||
and the retry-exhaustion path now return `idl_alloc(0)` (empty IDL).
|
||||
* Increase IDL_FETCH_RETRY_COUNT from 5 to 10 and replace the uniform
|
||||
random sleep with exponential backoff.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7126
|
||||
|
||||
Reviewed by: @tbordaz, @droideck, @progier389 (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/back-ldbm.h | 2 +-
|
||||
.../slapd/back-ldbm/db-bdb/bdb_ldif2db.c | 5 +-
|
||||
ldap/servers/slapd/back-ldbm/index.c | 51 +++++++++++++++----
|
||||
3 files changed, 46 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
index 462d1763c..25cbfb59b 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
|
||||
@@ -165,7 +165,7 @@ typedef unsigned short u_int16_t;
|
||||
#define SUBLEN 3
|
||||
#define LDBM_CACHE_RETRY_COUNT 1000 /* Number of times we re-try a cache operation */
|
||||
#define RETRY_CACHE_LOCK 2 /* error code to signal a retry of the cache lock */
|
||||
-#define IDL_FETCH_RETRY_COUNT 5 /* Number of times we re-try idl_fetch if it returns deadlock */
|
||||
+#define IDL_FETCH_RETRY_COUNT 10 /* Number of times we re-try idl_fetch if it returns deadlock */
|
||||
#define IMPORT_SUBCOUNT_HASHTABLE_SIZE 500 /* Number of buckets in hash used to accumulate subcount for broody parents */
|
||||
|
||||
/* minimum max ids that a single index entry can map to in ldbm */
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
index 7972850e9..7d904df91 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_ldif2db.c
|
||||
@@ -531,8 +531,8 @@ bdb_fetch_subtrees(backend *be, char **include, int *err)
|
||||
*err = ldbm_ancestorid_read(be, txn, id, &idl);
|
||||
}
|
||||
slapi_sdn_done(&sdn);
|
||||
- if (idl == NULL) {
|
||||
- if (DB_NOTFOUND == *err) {
|
||||
+ if (idl == NULL || IDL_NIDS(idl) == 0) {
|
||||
+ if (*err == 0 || DB_NOTFOUND == *err) {
|
||||
slapi_log_err(SLAPI_LOG_BACKLDBM,
|
||||
"bdb_fetch_subtrees", "Entry id %u has no descendants according to %s. "
|
||||
"Index file created by this reindex will be empty.\n",
|
||||
@@ -543,6 +543,7 @@ bdb_fetch_subtrees(backend *be, char **include, int *err)
|
||||
"bdb_fetch_subtrees", "%s not indexed on %u\n",
|
||||
entryrdn_get_noancestorid() ? "entryrdn" : "ancestorid", id);
|
||||
}
|
||||
+ idl_free(&idl);
|
||||
continue;
|
||||
}
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
|
||||
index 30fa09ebb..b22dda3c3 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/index.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/index.c
|
||||
@@ -932,7 +932,7 @@ index_read_ext_allids(
|
||||
prefix = index_index2prefix(indextype);
|
||||
if (prefix == NULL) {
|
||||
slapi_log_err(SLAPI_LOG_ERR, "index_read_ext_allids", "NULL prefix\n");
|
||||
- return NULL;
|
||||
+ return idl_alloc(0);
|
||||
}
|
||||
if (slapi_is_loglevel_set(LDAP_DEBUG_TRACE)) {
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids", "=> ( \"%s\" %s \"%s\" )\n",
|
||||
@@ -948,7 +948,7 @@ index_read_ext_allids(
|
||||
if (ai == NULL) {
|
||||
index_free_prefix(prefix);
|
||||
slapi_ch_free_string(&basetmp);
|
||||
- return NULL;
|
||||
+ return idl_alloc(0);
|
||||
}
|
||||
|
||||
slapi_log_err(SLAPI_LOG_ARGS, "index_read_ext_allids", "indextype: \"%s\" indexmask: 0x%x\n",
|
||||
@@ -967,7 +967,7 @@ index_read_ext_allids(
|
||||
slapi_ch_free_string(&basetmp);
|
||||
if (NULL == val || NULL == val->bv_val) {
|
||||
/* entrydn value was not given */
|
||||
- return NULL;
|
||||
+ return idl_alloc(0);
|
||||
}
|
||||
slapi_sdn_init_dn_byval(&sdn, val->bv_val);
|
||||
rc = entryrdn_index_read(be, &sdn, &id, txn);
|
||||
@@ -976,11 +976,11 @@ index_read_ext_allids(
|
||||
/* return an empty list */
|
||||
return idl_alloc(0);
|
||||
} else if (rc) { /* failure */
|
||||
- return NULL;
|
||||
+ return idl_alloc(0);
|
||||
} else { /* success */
|
||||
rc = idl_append_extend(&idl, id);
|
||||
if (rc) { /* failure */
|
||||
- return NULL;
|
||||
+ return idl_alloc(0);
|
||||
}
|
||||
return idl;
|
||||
}
|
||||
@@ -1022,11 +1022,11 @@ index_read_ext_allids(
|
||||
}
|
||||
if ((*err = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE)) != 0) {
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids",
|
||||
- "<= NULL (index file open for attr %s)\n",
|
||||
+ "<= empty IDL (index file open for attr %s)\n",
|
||||
basetype);
|
||||
index_free_prefix(prefix);
|
||||
slapi_ch_free_string(&basetmp);
|
||||
- return (NULL);
|
||||
+ return idl_alloc(0);
|
||||
}
|
||||
|
||||
if (val != NULL) {
|
||||
@@ -1067,12 +1067,32 @@ index_read_ext_allids(
|
||||
idl = idl_fetch_ext(be, db, &key, db_txn, ai, err, allidslimit);
|
||||
if (*err == DB_LOCK_DEADLOCK) {
|
||||
ldbm_nasty("index_read_ext_allids", "index read retrying transaction", 1045, *err);
|
||||
+ slapi_log_err(SLAPI_LOG_BACKLDBM, "index_read_ext_allids",
|
||||
+ "DBI_RC_RETRY on retry %d/%d for %s\n",
|
||||
+ retry_count + 1, IDL_FETCH_RETRY_COUNT, basetype);
|
||||
#ifdef FIX_TXN_DEADLOCKS
|
||||
#error can only retry here if txn == NULL - otherwise, have to abort and retry txn
|
||||
#endif
|
||||
- interval = PR_MillisecondsToInterval(slapi_rand() % 100);
|
||||
+ /* Exponential backoff with jitter, capped at 200ms */
|
||||
+ {
|
||||
+ PRUint32 backoff_ms = 10;
|
||||
+ int i;
|
||||
+ for (i = 0; i < retry_count && backoff_ms < 200; i++) {
|
||||
+ backoff_ms *= 2;
|
||||
+ }
|
||||
+ if (backoff_ms > 200) {
|
||||
+ backoff_ms = 200;
|
||||
+ }
|
||||
+ backoff_ms += slapi_rand() % (backoff_ms + 1);
|
||||
+ interval = PR_MillisecondsToInterval(backoff_ms);
|
||||
+ }
|
||||
DS_Sleep(interval);
|
||||
continue;
|
||||
+ } else if (*err == DB_NOTFOUND) {
|
||||
+ /* Key not found in index - this is normal, not an error */
|
||||
+ idl_free(&idl);
|
||||
+ idl = idl_alloc(0);
|
||||
+ break;
|
||||
} else if (*err != 0 || idl == NULL) {
|
||||
/* The database might not exist. We have to assume it means empty set */
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids", "Failed to access idl index for %s\n", basetype);
|
||||
@@ -1086,10 +1106,13 @@ index_read_ext_allids(
|
||||
}
|
||||
if (retry_count == IDL_FETCH_RETRY_COUNT) {
|
||||
ldbm_nasty("index_read_ext_allids", "index_read retry count exceeded", 1046, *err);
|
||||
+ /* Ensure we don't return NULL after exhausting retries */
|
||||
+ if (idl == NULL) {
|
||||
+ idl = idl_alloc(0);
|
||||
+ }
|
||||
} else if (*err != 0 && *err != DB_NOTFOUND) {
|
||||
ldbm_nasty("index_read_ext_allids", errmsg, 1050, *err);
|
||||
}
|
||||
- slapi_ch_free_string(&basetmp);
|
||||
slapi_ch_free_string(&tmpbuf);
|
||||
|
||||
dblayer_release_index_file(be, ai, db);
|
||||
@@ -1100,6 +1123,16 @@ index_read_ext_allids(
|
||||
ber_bvfree(encrypted_val);
|
||||
}
|
||||
|
||||
+ /* Ensure we never return NULL - always return valid IDL */
|
||||
+ if (idl == NULL) {
|
||||
+ slapi_log_err(SLAPI_LOG_WARNING, "index_read_ext_allids",
|
||||
+ "Returning empty IDL for %s after error %d\n",
|
||||
+ basetype, *err);
|
||||
+ idl = idl_alloc(0);
|
||||
+ }
|
||||
+
|
||||
+ slapi_ch_free_string(&basetmp);
|
||||
+
|
||||
slapi_log_err(SLAPI_LOG_TRACE, "index_read_ext_allids", "<= %lu candidates\n",
|
||||
(u_long)IDL_NIDS(idl));
|
||||
return (idl);
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
From 92e89927c9ed213d24c79f3cc62dc65027e09b99 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Tue, 9 Dec 2025 11:46:11 +0100
|
||||
Subject: [PATCH 03/13] Issue 7124 - (2nd) BDB cursor race condition with
|
||||
transaction isolation
|
||||
|
||||
Description:
|
||||
1.4.x. only.
|
||||
Add checks to idl_new_fetch() and idl_new_range_fetch() if the
|
||||
database's environment actually supports transactions.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7124
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 28 ++++++++++++++++++++++++++
|
||||
1 file changed, 28 insertions(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 212f31b7b..205d0007e 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -163,6 +163,20 @@ idl_new_fetch(
|
||||
dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Verify the database's environment actually supports transactions.
|
||||
+ * During import, databases are opened with a private environment that
|
||||
+ * lacks DB_INIT_TXN. If we try to use a transaction from the main
|
||||
+ * environment with a database from the import environment, BDB will fail.
|
||||
+ * Clear the txn if the database's environment is NULL or doesn't support
|
||||
+ * transactions.
|
||||
+ */
|
||||
+ if (s_txn.back_txn_txn != NULL) {
|
||||
+ if (db->dbenv == NULL || !dblayer_db_uses_transactions(db->dbenv)) {
|
||||
+ s_txn.back_txn_txn = NULL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Make a cursor */
|
||||
ret = db->cursor(db, txn, &cursor, 0);
|
||||
if (0 != ret) {
|
||||
@@ -410,6 +424,20 @@ idl_new_range_fetch(
|
||||
dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Verify the database's environment actually supports transactions.
|
||||
+ * During import, databases are opened with a private environment that
|
||||
+ * lacks DB_INIT_TXN. If we try to use a transaction from the main
|
||||
+ * environment with a database from the import environment, BDB will fail.
|
||||
+ * Clear the txn if the database's environment is NULL or doesn't support
|
||||
+ * transactions.
|
||||
+ */
|
||||
+ if (s_txn.back_txn_txn != NULL) {
|
||||
+ if (db->dbenv == NULL || !dblayer_db_uses_transactions(db->dbenv)) {
|
||||
+ s_txn.back_txn_txn = NULL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Make a cursor */
|
||||
ret = db->cursor(db, txn, &cursor, 0);
|
||||
if (0 != ret) {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,682 @@
|
||||
From d53726c4c04e8d4844ffe0a78b9713e938e1a348 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 29 Nov 2025 22:45:21 +0100
|
||||
Subject: [PATCH 04/13] Issue 7124 - BDB cursor race condition with transaction
|
||||
isolation (#7125)
|
||||
|
||||
Bug Description:
|
||||
ASAN reported crashes in `__db_ditem_nolog()` with negative-size-param
|
||||
errors. Cursor operations without transaction isolation allowed
|
||||
concurrent page modifications to corrupt cursor state, leading to
|
||||
invalid memory access.
|
||||
|
||||
The race condition occurs when:
|
||||
1. T1 opens a cursor without transaction protection
|
||||
2. T2 modifies the same index page
|
||||
3. T1 cursor operates on stale page metadata
|
||||
4. `__db_ditem_nolog()` calculates negative size for `memmove()`
|
||||
5. Crash: `AddressSanitizer: negative-size-param: (size=-8)`
|
||||
|
||||
Reproducer: dirsrvtests/tests/stress/backend/bdb_cursor_race_test.py
|
||||
Crash under ASAN usually happens within 10-30 minutes, but sometimes it
|
||||
can run for hours without any crash.
|
||||
|
||||
Fix Description:
|
||||
Implement transaction isolation for cursors in `idl_new_fetch()` and
|
||||
`idl_new_range_fetch()` by always calling `dblayer_read_txn_begin()`.
|
||||
|
||||
In `bdb_txn_begin()` verify if the environment supports transactions
|
||||
(has DB_INIT_TXN flag) before attempting to begin a transaction.
|
||||
This prevents errors during offline import which uses a private
|
||||
environment without transaction support.
|
||||
|
||||
In `bdb_public_new_cursor()` skip transaction usage when the database's
|
||||
environment doesn't support transactions.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7124
|
||||
|
||||
Reviewed by: @progier389, @tbordaz (Thanks!)
|
||||
---
|
||||
.../stress/backend/bdb_cursor_race_test.py | 544 ++++++++++++++++++
|
||||
.../slapd/back-ldbm/db-bdb/bdb_layer.c | 13 +-
|
||||
ldap/servers/slapd/back-ldbm/idl_new.c | 24 +-
|
||||
3 files changed, 571 insertions(+), 10 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/stress/backend/bdb_cursor_race_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/stress/backend/bdb_cursor_race_test.py b/dirsrvtests/tests/stress/backend/bdb_cursor_race_test.py
|
||||
new file mode 100644
|
||||
index 000000000..54a20e000
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/stress/backend/bdb_cursor_race_test.py
|
||||
@@ -0,0 +1,544 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2025 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+#
|
||||
+
|
||||
+import pytest
|
||||
+import logging
|
||||
+import ldap
|
||||
+import ldap.modlist as modlist
|
||||
+from threading import Thread, Event, Lock
|
||||
+import random
|
||||
+import time
|
||||
+from lib389.topologies import topology_st
|
||||
+from lib389._constants import DEFAULT_SUFFIX, DN_DM, PW_DM
|
||||
+from lib389.idm.user import UserAccounts
|
||||
+from lib389.idm.organizationalunit import OrganizationalUnits
|
||||
+
|
||||
+pytestmark = pytest.mark.tier2
|
||||
+
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+# Test configuration
|
||||
+SEARCH_THREADS = 30
|
||||
+MODIFY_THREADS = 15
|
||||
+DELETE_THREADS = 8
|
||||
+CHURN_THREADS = 5
|
||||
+DEFAULT_TIMEOUT = 60 * 60 # 60 minutes in seconds
|
||||
+
|
||||
+# Statistics and crash detection
|
||||
+stats_lock = Lock()
|
||||
+stats = {
|
||||
+ 'searches': 0,
|
||||
+ 'modifies': 0,
|
||||
+ 'deletes': 0,
|
||||
+ 'adds': 0,
|
||||
+ 'errors': 0,
|
||||
+ 'start_time': None
|
||||
+}
|
||||
+
|
||||
+# Global flag to track if server crashed
|
||||
+server_crashed = False
|
||||
+crash_lock = Lock()
|
||||
+
|
||||
+HIGH_CONTENTION_FILTERS = [
|
||||
+ "(objectClass=inetOrgPerson)",
|
||||
+ "(objectClass=person)",
|
||||
+ "(objectClass=*)",
|
||||
+ "(uid=*)",
|
||||
+ "(cn=*)",
|
||||
+ "(mail=*)",
|
||||
+ "(description=*)",
|
||||
+ "(telephoneNumber=*)",
|
||||
+]
|
||||
+
|
||||
+
|
||||
+def create_raw_connection(host, port):
|
||||
+ """Create a raw LDAP connection independent of the instance connection pool
|
||||
+
|
||||
+ :param host: LDAP server hostname
|
||||
+ :param port: LDAP server port
|
||||
+ :returns: Raw LDAP connection
|
||||
+ """
|
||||
+ # Use ldap.initialize to create a completely independent connection
|
||||
+ uri = f"ldap://{host}:{port}"
|
||||
+ conn = ldap.initialize(uri)
|
||||
+ conn.protocol_version = ldap.VERSION3
|
||||
+
|
||||
+ # Short timeout to fail fast
|
||||
+ conn.set_option(ldap.OPT_NETWORK_TIMEOUT, 5.0)
|
||||
+ conn.set_option(ldap.OPT_TIMEOUT, 5.0)
|
||||
+
|
||||
+ # Bind as Directory Manager
|
||||
+ conn.simple_bind_s(DN_DM, PW_DM)
|
||||
+ return conn
|
||||
+
|
||||
+
|
||||
+def update_stats(operation, count=1):
|
||||
+ """Update operation statistics in a thread-safe manner"""
|
||||
+ with stats_lock:
|
||||
+ stats[operation] = stats.get(operation, 0) + count
|
||||
+
|
||||
+
|
||||
+def mark_server_crashed():
|
||||
+ """Mark that the server has crashed"""
|
||||
+ global server_crashed
|
||||
+ with crash_lock:
|
||||
+ server_crashed = True
|
||||
+
|
||||
+
|
||||
+def print_stats():
|
||||
+ """Print current statistics"""
|
||||
+ with stats_lock:
|
||||
+ if stats['start_time']:
|
||||
+ elapsed = time.time() - stats['start_time']
|
||||
+ log.info(f"Statistics after {elapsed:.1f}s: "
|
||||
+ f"searches={stats['searches']}, "
|
||||
+ f"modifies={stats['modifies']}, "
|
||||
+ f"adds={stats['adds']}, "
|
||||
+ f"deletes={stats['deletes']}, "
|
||||
+ f"errors={stats['errors']}")
|
||||
+
|
||||
+
|
||||
+def setup_test_data(inst, num_entries=1000):
|
||||
+ """Setup test data with multi-valued attributes
|
||||
+
|
||||
+ :param inst: DirSrv instance
|
||||
+ :param num_entries: Number of test entries to create
|
||||
+ """
|
||||
+ log.info(f"Setting up {num_entries} test entries with multi-valued attributes...")
|
||||
+
|
||||
+ # Create entries with many multi-valued attributes using raw LDAP
|
||||
+ created = 0
|
||||
+ for i in range(num_entries):
|
||||
+ try:
|
||||
+ dn = f"uid=test{i},ou=people,{DEFAULT_SUFFIX}"
|
||||
+
|
||||
+ descriptions = [f"description_{i}_{j}_{'x'*50}".encode() for j in range(20)]
|
||||
+ phones = [f"+1-555-{i:04d}-{j:04d}".encode() for j in range(10)]
|
||||
+ mails = [f"test{i}.{j}@example.com".encode() for j in range(5)]
|
||||
+
|
||||
+ attrs = {
|
||||
+ 'objectClass': [b'top', b'person', b'organizationalPerson', b'inetOrgPerson'],
|
||||
+ 'uid': [f'test{i}'.encode()],
|
||||
+ 'cn': [f'Test User {i}'.encode()],
|
||||
+ 'sn': [f'User{i}'.encode()],
|
||||
+ 'description': descriptions,
|
||||
+ 'telephoneNumber': phones,
|
||||
+ 'mail': mails,
|
||||
+ 'userPassword': [b'password'],
|
||||
+ }
|
||||
+
|
||||
+ inst.add_s(dn, modlist.addModlist(attrs))
|
||||
+ created += 1
|
||||
+
|
||||
+ if (i + 1) % 100 == 0:
|
||||
+ log.info(f"Created {i + 1}/{num_entries} entries...")
|
||||
+
|
||||
+ except ldap.ALREADY_EXISTS:
|
||||
+ pass
|
||||
+ except Exception as e:
|
||||
+ log.warning(f"Error creating entry {i}: {e}")
|
||||
+
|
||||
+ log.info(f"Test data setup complete. Created {created} new entries.")
|
||||
+
|
||||
+
|
||||
+def search_worker(stop_event, host, port, thread_id):
|
||||
+ """Worker thread for high-contention searches
|
||||
+
|
||||
+ :param stop_event: Event to signal thread termination
|
||||
+ :param host: LDAP server hostname
|
||||
+ :param port: LDAP server port
|
||||
+ :param thread_id: Thread identifier
|
||||
+ """
|
||||
+ # Create independent raw LDAP connection for this thread
|
||||
+ conn = create_raw_connection(host, port)
|
||||
+ log.info(f"Search worker {thread_id} started")
|
||||
+ search_count = 0
|
||||
+
|
||||
+ try:
|
||||
+ while not stop_event.is_set():
|
||||
+ try:
|
||||
+ filter_str = random.choice(HIGH_CONTENTION_FILTERS)
|
||||
+ scope = random.choice([ldap.SCOPE_SUBTREE, ldap.SCOPE_ONELEVEL])
|
||||
+
|
||||
+ conn.search_s(
|
||||
+ DEFAULT_SUFFIX,
|
||||
+ scope,
|
||||
+ filter_str,
|
||||
+ attrlist=['dn']
|
||||
+ )
|
||||
+
|
||||
+ search_count += 1
|
||||
+
|
||||
+ if search_count % 100 == 0:
|
||||
+ update_stats('searches', 100)
|
||||
+
|
||||
+ except ldap.SERVER_DOWN:
|
||||
+ log.error(f"Search worker {thread_id}: SERVER DOWN - ns-slapd crashed!")
|
||||
+ mark_server_crashed()
|
||||
+ stop_event.set()
|
||||
+ break
|
||||
+ except Exception as e:
|
||||
+ update_stats('errors')
|
||||
+ if search_count < 10:
|
||||
+ log.debug(f"Search worker {thread_id} error: {e}")
|
||||
+ finally:
|
||||
+ try:
|
||||
+ conn.unbind_s()
|
||||
+ except:
|
||||
+ pass
|
||||
+ log.info(f"Search worker {thread_id} stopped (total searches: {search_count})")
|
||||
+
|
||||
+
|
||||
+def modify_worker(stop_event, host, port, thread_id):
|
||||
+ """Worker thread for modifying multi-valued attributes
|
||||
+
|
||||
+ :param stop_event: Event to signal thread termination
|
||||
+ :param host: LDAP server hostname
|
||||
+ :param port: LDAP server port
|
||||
+ :param thread_id: Thread identifier
|
||||
+ """
|
||||
+ # Create independent raw LDAP connection for this thread
|
||||
+ conn = create_raw_connection(host, port)
|
||||
+ log.info(f"Modify worker {thread_id} started")
|
||||
+ modify_count = 0
|
||||
+
|
||||
+ try:
|
||||
+ while not stop_event.is_set():
|
||||
+ try:
|
||||
+ entry_num = random.randint(0, 999)
|
||||
+ dn = f"uid=test{entry_num},ou=people,{DEFAULT_SUFFIX}"
|
||||
+ attr = 'description'
|
||||
+ value = f"{attr}_{int(time.time() * 1000000)}_{'x'*100}"
|
||||
+
|
||||
+ # Strategy 1: Add a new value (increases index size)
|
||||
+ if random.random() < 0.3:
|
||||
+ mod_attrs = [(ldap.MOD_ADD, attr, [value.encode()])]
|
||||
+
|
||||
+ # Strategy 2: Replace all values (causes delete + add)
|
||||
+ # This should trigger __db_ditem_nolog() in the delete phase
|
||||
+ elif random.random() < 0.7:
|
||||
+ new_values = [f"{attr}_new_{i}_{'y'*100}".encode() for i in range(20)]
|
||||
+ mod_attrs = [(ldap.MOD_REPLACE, attr, new_values)]
|
||||
+
|
||||
+ # Strategy 3: Delete all values, then add multiple new ones
|
||||
+ else:
|
||||
+ new_values = [f"{attr}_churn_{i}_{'z'*100}".encode() for i in range(15)]
|
||||
+ mod_attrs = [
|
||||
+ (ldap.MOD_DELETE, attr, []),
|
||||
+ (ldap.MOD_ADD, attr, new_values)
|
||||
+ ]
|
||||
+
|
||||
+ conn.modify_s(dn, mod_attrs)
|
||||
+ modify_count += 1
|
||||
+
|
||||
+ if modify_count % 50 == 0:
|
||||
+ update_stats('modifies', 50)
|
||||
+
|
||||
+ except ldap.SERVER_DOWN:
|
||||
+ log.error(f"Modify worker {thread_id}: SERVER DOWN - ns-slapd crashed!")
|
||||
+ mark_server_crashed()
|
||||
+ stop_event.set()
|
||||
+ break
|
||||
+ except ldap.NO_SUCH_OBJECT:
|
||||
+ pass
|
||||
+ except Exception as e:
|
||||
+ update_stats('errors')
|
||||
+ if modify_count < 10:
|
||||
+ log.debug(f"Modify worker {thread_id} error: {e}")
|
||||
+ finally:
|
||||
+ try:
|
||||
+ conn.unbind_s()
|
||||
+ except:
|
||||
+ pass
|
||||
+ log.info(f"Modify worker {thread_id} stopped (total modifies: {modify_count})")
|
||||
+
|
||||
+
|
||||
+def delete_worker(stop_event, host, port, thread_id):
|
||||
+ """Worker thread for deleting entries
|
||||
+
|
||||
+ :param stop_event: Event to signal thread termination
|
||||
+ :param host: LDAP server hostname
|
||||
+ :param port: LDAP server port
|
||||
+ :param thread_id: Thread identifier
|
||||
+ """
|
||||
+ # Create independent raw LDAP connection for this thread
|
||||
+ conn = create_raw_connection(host, port)
|
||||
+ log.info(f"Delete worker {thread_id} started")
|
||||
+ delete_count = 0
|
||||
+ delete_range_start = 5000 + (thread_id * 1000)
|
||||
+
|
||||
+ try:
|
||||
+ while not stop_event.is_set():
|
||||
+ try:
|
||||
+ entry_num = delete_range_start + (delete_count % 1000)
|
||||
+ dn = f"uid=delete{entry_num},ou=people,{DEFAULT_SUFFIX}"
|
||||
+
|
||||
+ conn.delete_s(dn)
|
||||
+ delete_count += 1
|
||||
+
|
||||
+ if delete_count % 20 == 0:
|
||||
+ update_stats('deletes', 20)
|
||||
+
|
||||
+ time.sleep(0.01)
|
||||
+
|
||||
+ except ldap.SERVER_DOWN:
|
||||
+ log.error(f"Delete worker {thread_id}: SERVER DOWN - ns-slapd crashed!")
|
||||
+ mark_server_crashed()
|
||||
+ stop_event.set()
|
||||
+ break
|
||||
+ except ldap.NO_SUCH_OBJECT:
|
||||
+ pass
|
||||
+ except Exception as e:
|
||||
+ update_stats('errors')
|
||||
+ if delete_count < 10:
|
||||
+ log.debug(f"Delete worker {thread_id} error: {e}")
|
||||
+ finally:
|
||||
+ try:
|
||||
+ conn.unbind_s()
|
||||
+ except:
|
||||
+ pass
|
||||
+ log.info(f"Delete worker {thread_id} stopped (total deletes: {delete_count})")
|
||||
+
|
||||
+
|
||||
+def churn_worker(stop_event, host, port, thread_id):
|
||||
+ """Worker thread for add/delete churn
|
||||
+
|
||||
+ :param stop_event: Event to signal thread termination
|
||||
+ :param host: LDAP server hostname
|
||||
+ :param port: LDAP server port
|
||||
+ :param thread_id: Thread identifier
|
||||
+ """
|
||||
+ # Create independent raw LDAP connection for this thread
|
||||
+ conn = create_raw_connection(host, port)
|
||||
+ log.info(f"Churn worker {thread_id} started")
|
||||
+ counter = 10000 + (thread_id * 10000)
|
||||
+ add_count = 0
|
||||
+
|
||||
+ try:
|
||||
+ while not stop_event.is_set():
|
||||
+ try:
|
||||
+ dn = f"uid=churn{counter},ou=people,{DEFAULT_SUFFIX}"
|
||||
+
|
||||
+ attrs = {
|
||||
+ 'objectClass': [b'top', b'person', b'inetOrgPerson'],
|
||||
+ 'uid': [f'churn{counter}'.encode()],
|
||||
+ 'cn': [f'Churn {counter}'.encode()],
|
||||
+ 'sn': [f'User{counter}'.encode()],
|
||||
+ 'description': [f'desc_{i}'.encode() for i in range(15)],
|
||||
+ 'telephoneNumber': [f'+1-555-churn-{i:04d}'.encode() for i in range(8)],
|
||||
+ 'userPassword': [b'password'],
|
||||
+ }
|
||||
+
|
||||
+ conn.add_s(dn, modlist.addModlist(attrs))
|
||||
+ add_count += 1
|
||||
+ update_stats('adds')
|
||||
+
|
||||
+ time.sleep(0.005)
|
||||
+
|
||||
+ conn.delete_s(dn)
|
||||
+ update_stats('deletes')
|
||||
+
|
||||
+ counter += 1
|
||||
+
|
||||
+ time.sleep(0.01)
|
||||
+
|
||||
+ except ldap.SERVER_DOWN:
|
||||
+ log.error(f"Churn worker {thread_id}: SERVER DOWN - ns-slapd crashed!")
|
||||
+ mark_server_crashed()
|
||||
+ stop_event.set()
|
||||
+ break
|
||||
+ except ldap.ALREADY_EXISTS:
|
||||
+ try:
|
||||
+ conn.delete_s(dn)
|
||||
+ except:
|
||||
+ pass
|
||||
+ counter += 1
|
||||
+ except Exception as e:
|
||||
+ update_stats('errors')
|
||||
+ if add_count < 10:
|
||||
+ log.debug(f"Churn worker {thread_id} error: {e}")
|
||||
+ counter += 1
|
||||
+ finally:
|
||||
+ try:
|
||||
+ conn.unbind_s()
|
||||
+ except:
|
||||
+ pass
|
||||
+ log.info(f"Churn worker {thread_id} stopped (total adds: {add_count})")
|
||||
+
|
||||
+
|
||||
+def monitor_worker(stop_event, duration):
|
||||
+ """Worker thread to monitor progress and enforce timeout
|
||||
+
|
||||
+ :param stop_event: Event to signal thread termination
|
||||
+ :param duration: Test duration in seconds
|
||||
+ """
|
||||
+ log.info(f"Monitor worker started (duration: {duration}s / {duration/60:.1f}m)")
|
||||
+
|
||||
+ start_time = time.time()
|
||||
+ last_stats_time = start_time
|
||||
+
|
||||
+ while not stop_event.is_set():
|
||||
+ current_time = time.time()
|
||||
+ elapsed = current_time - start_time
|
||||
+
|
||||
+ # Print stats every 30 seconds
|
||||
+ if current_time - last_stats_time >= 30:
|
||||
+ print_stats()
|
||||
+ log.info(f"Progress: {elapsed:.0f}s / {duration}s ({elapsed/duration*100:.1f}%)")
|
||||
+ last_stats_time = current_time
|
||||
+
|
||||
+ # Check if duration has been reached
|
||||
+ if elapsed >= duration:
|
||||
+ log.info(f"Duration {duration}s reached. Test completed successfully!")
|
||||
+ stop_event.set()
|
||||
+ break
|
||||
+
|
||||
+ time.sleep(1)
|
||||
+
|
||||
+ log.info("Monitor worker stopped")
|
||||
+
|
||||
+
|
||||
+@pytest.mark.slow
|
||||
+def test_bdb_cursor_race(topology_st):
|
||||
+ """Stress test to reproduce BDB cursor race condition
|
||||
+
|
||||
+ :id: d53a9580-58e5-454c-96fd-57839660fd51
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Setup test data with 1000 entries containing multi-valued attributes
|
||||
+ 2. Start search, modify, delete and churn threads
|
||||
+ 3. Run for 60 minutes
|
||||
+ :expectedresults:
|
||||
+ 1. Success
|
||||
+ 2. Success
|
||||
+ 3. Server doesn't crash
|
||||
+ """
|
||||
+
|
||||
+ inst = topology_st.standalone
|
||||
+
|
||||
+ log.info("=" * 80)
|
||||
+ log.info("BDB Cursor Race Condition Test")
|
||||
+ log.info(f"Instance: {inst.serverid}")
|
||||
+ log.info(f"Base DN: {DEFAULT_SUFFIX}")
|
||||
+ log.info(f"Duration: {DEFAULT_TIMEOUT}s ({DEFAULT_TIMEOUT/60:.0f} minutes)")
|
||||
+ log.info("=" * 80)
|
||||
+
|
||||
+ # Enable log buffering for better performance
|
||||
+ log.info("Enabling log buffering for access and error logs...")
|
||||
+ inst.config.set('nsslapd-accesslog-logbuffering', 'on')
|
||||
+ inst.config.set('nsslapd-errorlog-logbuffering', 'on')
|
||||
+
|
||||
+ # Setup test data
|
||||
+ setup_test_data(inst, num_entries=1000)
|
||||
+
|
||||
+ # Extract connection parameters once to avoid sharing inst object
|
||||
+ ldap_host = inst.host
|
||||
+ ldap_port = inst.port
|
||||
+
|
||||
+ # Reset statistics and crash flag
|
||||
+ global stats, server_crashed
|
||||
+ stats = {
|
||||
+ 'searches': 0,
|
||||
+ 'modifies': 0,
|
||||
+ 'deletes': 0,
|
||||
+ 'adds': 0,
|
||||
+ 'errors': 0,
|
||||
+ 'start_time': time.time()
|
||||
+ }
|
||||
+ server_crashed = False
|
||||
+
|
||||
+ # Create stop event
|
||||
+ stop_event = Event()
|
||||
+
|
||||
+ # Start all worker threads
|
||||
+ threads = []
|
||||
+
|
||||
+ # Search workers - hammer equality indexes
|
||||
+ for i in range(SEARCH_THREADS):
|
||||
+ t = Thread(target=search_worker,
|
||||
+ args=(stop_event, ldap_host, ldap_port, i),
|
||||
+ name=f"search-{i}")
|
||||
+ t.daemon = True
|
||||
+ t.start()
|
||||
+ threads.append(t)
|
||||
+
|
||||
+ # Modify workers - update multi-valued attributes
|
||||
+ for i in range(MODIFY_THREADS):
|
||||
+ t = Thread(target=modify_worker,
|
||||
+ args=(stop_event, ldap_host, ldap_port, i),
|
||||
+ name=f"modify-{i}")
|
||||
+ t.daemon = True
|
||||
+ t.start()
|
||||
+ threads.append(t)
|
||||
+
|
||||
+ # Delete workers - trigger index cleanup
|
||||
+ for i in range(DELETE_THREADS):
|
||||
+ t = Thread(target=delete_worker,
|
||||
+ args=(stop_event, ldap_host, ldap_port, i),
|
||||
+ name=f"delete-{i}")
|
||||
+ t.daemon = True
|
||||
+ t.start()
|
||||
+ threads.append(t)
|
||||
+
|
||||
+ # Churn workers - add/delete cycle
|
||||
+ for i in range(CHURN_THREADS):
|
||||
+ t = Thread(target=churn_worker,
|
||||
+ args=(stop_event, ldap_host, ldap_port, i),
|
||||
+ name=f"churn-{i}")
|
||||
+ t.daemon = True
|
||||
+ t.start()
|
||||
+ threads.append(t)
|
||||
+
|
||||
+ # Monitor worker - enforce timeout and print stats
|
||||
+ monitor_thread = Thread(target=monitor_worker,
|
||||
+ args=(stop_event, DEFAULT_TIMEOUT),
|
||||
+ name="monitor")
|
||||
+ monitor_thread.daemon = True
|
||||
+ monitor_thread.start()
|
||||
+ threads.append(monitor_thread)
|
||||
+
|
||||
+ log.info("=" * 80)
|
||||
+ log.info(f"Started {len(threads)} worker threads:")
|
||||
+ log.info(f" - {SEARCH_THREADS} search threads (equality index queries)")
|
||||
+ log.info(f" - {MODIFY_THREADS} modify threads (multi-valued attr updates)")
|
||||
+ log.info(f" - {DELETE_THREADS} delete threads (index cleanup)")
|
||||
+ log.info(f" - {CHURN_THREADS} churn threads (add/delete cycle)")
|
||||
+ log.info(f" - 1 monitor thread (timeout enforcement)")
|
||||
+ log.info("=" * 80)
|
||||
+ log.info(f"Running for {DEFAULT_TIMEOUT}s ({DEFAULT_TIMEOUT/60:.0f} minutes)...")
|
||||
+ log.info("=" * 80)
|
||||
+
|
||||
+ # Wait for all threads to finish or timeout
|
||||
+ for t in threads:
|
||||
+ t.join()
|
||||
+
|
||||
+ # Final statistics
|
||||
+ log.info("=" * 80)
|
||||
+ log.info("Final Statistics:")
|
||||
+ print_stats()
|
||||
+ log.info("=" * 80)
|
||||
+
|
||||
+ # Check if server crashed during the test
|
||||
+ if server_crashed:
|
||||
+ log.error("=" * 80)
|
||||
+ log.error("Test FAILED: Server crashed during the test!")
|
||||
+ log.error("=" * 80)
|
||||
+ pytest.fail("ns-slapd crashed during BDB cursor race test")
|
||||
+ else:
|
||||
+ log.info("=" * 80)
|
||||
+ log.info("Test PASSED: Completed full duration without crash")
|
||||
+ log.info("=" * 80)
|
||||
+
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ # Run isolated
|
||||
+ # -s for DEBUG mode
|
||||
+ import os
|
||||
+ CURRENT_FILE = os.path.realpath(__file__)
|
||||
+ pytest.main(["-s", "-v", CURRENT_FILE])
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
index 651e595ee..a2dcc61e1 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
@@ -2634,10 +2634,19 @@ bdb_txn_begin(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn, PRBool
|
||||
txn->back_txn_txn = NULL;
|
||||
}
|
||||
|
||||
- if (conf->bdb_enable_transactions) {
|
||||
+ bdb_db_env *pEnv = (bdb_db_env *)priv->dblayer_env;
|
||||
+
|
||||
+ /*
|
||||
+ * Check both config and actual environment capabilities before starting a transaction.
|
||||
+ * During offline import, the environment is created without DB_INIT_TXN even though
|
||||
+ * bdb_enable_transactions may be true in config. We use bdb_openflags (cached at
|
||||
+ * environment open time) rather than get_open_flags() to safely handle cases where
|
||||
+ * the environment handle exists but may not be fully opened yet.
|
||||
+ */
|
||||
+ if (pEnv && pEnv->bdb_DB_ENV && conf->bdb_enable_transactions &&
|
||||
+ (pEnv->bdb_openflags & DB_INIT_TXN)) {
|
||||
int txn_begin_flags;
|
||||
|
||||
- bdb_db_env *pEnv = (bdb_db_env *)priv->dblayer_env;
|
||||
if (use_lock)
|
||||
slapi_rwlock_rdlock(pEnv->bdb_env_lock);
|
||||
if (!parent_txn) {
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
index 205d0007e..fff2c805e 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
|
||||
@@ -158,10 +158,14 @@ idl_new_fetch(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Always use transaction isolation for cursor operations to prevent
|
||||
+ * race conditions. Without a transaction, concurrent modifications to
|
||||
+ * index pages can corrupt cursor state, leading to crashes in BDB's
|
||||
+ * internal functions (e.g., negative size passed to memmove).
|
||||
+ */
|
||||
dblayer_txn_init(li, &s_txn);
|
||||
- if (txn) {
|
||||
- dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
- }
|
||||
+ dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
|
||||
/*
|
||||
* Verify the database's environment actually supports transactions.
|
||||
@@ -178,7 +182,7 @@ idl_new_fetch(
|
||||
}
|
||||
|
||||
/* Make a cursor */
|
||||
- ret = db->cursor(db, txn, &cursor, 0);
|
||||
+ ret = db->cursor(db, s_txn.back_txn_txn, &cursor, 0);
|
||||
if (0 != ret) {
|
||||
ldbm_nasty("idl_new_fetch", filename, 1, ret);
|
||||
cursor = NULL;
|
||||
@@ -419,10 +423,14 @@ idl_new_range_fetch(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Always use transaction isolation for cursor operations to prevent
|
||||
+ * race conditions. Without a transaction, concurrent modifications to
|
||||
+ * index pages can corrupt cursor state, leading to crashes in BDB's
|
||||
+ * internal functions (e.g., negative size passed to memmove).
|
||||
+ */
|
||||
dblayer_txn_init(li, &s_txn);
|
||||
- if (txn) {
|
||||
- dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
- }
|
||||
+ dblayer_read_txn_begin(be, txn, &s_txn);
|
||||
|
||||
/*
|
||||
* Verify the database's environment actually supports transactions.
|
||||
@@ -439,7 +447,7 @@ idl_new_range_fetch(
|
||||
}
|
||||
|
||||
/* Make a cursor */
|
||||
- ret = db->cursor(db, txn, &cursor, 0);
|
||||
+ ret = db->cursor(db, s_txn.back_txn_txn, &cursor, 0);
|
||||
if (0 != ret) {
|
||||
ldbm_nasty("idl_new_range_fetch", filename, 1, ret);
|
||||
cursor = NULL;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
From 6a159091cce5a2dedef9b3b9b64dfb5b332ec9c4 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Wed, 1 Apr 2026 23:04:56 +0100
|
||||
Subject: [PATCH 05/13] Issue 7380 - Internal op with negative wtime and large
|
||||
optime (#7381)
|
||||
|
||||
Description:
|
||||
The retro CL plugin triggers internal operations that log incorrect wtime and optime
|
||||
to the server access logs.This happens because seq_internal_callback_pb() calls the
|
||||
backend directly without setting the operation start time.
|
||||
|
||||
Fix:
|
||||
Add slapi_operation_set_time_started(op) to seq_internal_callback_pb() to record
|
||||
when the operation actually starts processing.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7380
|
||||
|
||||
Reviewed by: @mreynolds389 (Thank you)
|
||||
---
|
||||
ldap/servers/slapd/plugin_internal_op.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/plugin_internal_op.c b/ldap/servers/slapd/plugin_internal_op.c
|
||||
index a140e7988..38de6177e 100644
|
||||
--- a/ldap/servers/slapd/plugin_internal_op.c
|
||||
+++ b/ldap/servers/slapd/plugin_internal_op.c
|
||||
@@ -378,6 +378,7 @@ seq_internal_callback_pb(Slapi_PBlock *pb, void *callback_data, plugin_result_ca
|
||||
set_common_params(pb);
|
||||
|
||||
slapi_td_internal_op_start();
|
||||
+ slapi_operation_set_time_started(op);
|
||||
if (be->be_seq != NULL) {
|
||||
rc = (*be->be_seq)(pb);
|
||||
} else {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
From 159eb43433002378ded06af6c30ea7aed8c220ad Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Wed, 14 Jan 2026 17:55:29 +0100
|
||||
Subject: [PATCH 06/13] Issue 7152 - ns-slapd fails to shutdown when deferred
|
||||
memberof update is in progress (#7187)
|
||||
|
||||
Bug Description:
|
||||
When a deferred memberof update is in progress during shutdown, the
|
||||
backend operations (add, modify, delete, modrdn) wait in a polling loop
|
||||
for the deferred task to complete. However, if the deferred thread exits
|
||||
before clearing the SLAPI_DEFERRED_MEMBEROF flag, the loop becomes
|
||||
infinite, causing the server to hang during shutdown.
|
||||
|
||||
Fix Description:
|
||||
Add additional check to the polling loops so they exit immediately when
|
||||
the server is shutting down.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7152
|
||||
|
||||
Reviewed by: @tbordaz (Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_add.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_delete.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modify.c | 2 +-
|
||||
ldap/servers/slapd/back-ldbm/ldbm_modrdn.c | 2 +-
|
||||
4 files changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
index bca660f22..226b846cb 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
@@ -1451,7 +1451,7 @@ common_return:
|
||||
if (deferred) {
|
||||
delay = PR_MillisecondsToInterval(100);
|
||||
}
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
index c0f2516de..cf79daa77 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
|
||||
@@ -1577,7 +1577,7 @@ diskfull_return:
|
||||
if (deferred) {
|
||||
delay = PR_MillisecondsToInterval(100);
|
||||
}
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
index 0c62c87f0..ccecce571 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
|
||||
@@ -1168,7 +1168,7 @@ common_return:
|
||||
if (deferred) {
|
||||
delay = PR_MillisecondsToInterval(100);
|
||||
}
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
index fe35991b5..31e2caa46 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
|
||||
@@ -1519,7 +1519,7 @@ common_return:
|
||||
if (deferred) {
|
||||
delay = PR_MillisecondsToInterval(100);
|
||||
}
|
||||
- while (deferred) {
|
||||
+ while (deferred && !g_get_shutdown()) {
|
||||
DS_Sleep(delay);
|
||||
slapi_pblock_get(pb, SLAPI_DEFERRED_MEMBEROF, &deferred);
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
From cb1caa0093381d7b52ea61931db7d33804a025b8 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 23 Feb 2026 12:37:26 -0500
|
||||
Subject: [PATCH 07/13] Issue 7271 - plugins that create threads need to update
|
||||
active thread count
|
||||
|
||||
Description:
|
||||
|
||||
Plugins that create threads need to up to the global active thread count.
|
||||
Otherwise when the server is being stopped the plugin's close function gets
|
||||
called while these threads are still running and still using the plugin
|
||||
configuration. This can lead to crashes.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier & tbordaz (Thanks!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_protocol.c | 5 +++++
|
||||
ldap/servers/plugins/retrocl/retrocl_trim.c | 7 ++++++-
|
||||
2 files changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
index dfdb69889..6cdbcc4f1 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "repl5.h"
|
||||
#include "repl5_prot_private.h"
|
||||
+#include "slap.h"
|
||||
|
||||
#define PROTOCOL_5_INCREMENTAL 1
|
||||
#define PROTOCOL_5_TOTAL 2
|
||||
@@ -237,6 +238,8 @@ prot_thread_main(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
set_thread_private_agmtname(agmt_get_long_name(agmt));
|
||||
|
||||
done = 0;
|
||||
@@ -301,6 +304,8 @@ prot_thread_main(void *arg)
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
index 158f801cc..6514fc790 100644
|
||||
--- a/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
@@ -243,6 +243,8 @@ trim_changelog(void)
|
||||
|
||||
now_interval = slapi_current_rel_time_t(); /* monotonic time for interval */
|
||||
|
||||
+ g_incr_active_threadcnt();
|
||||
+
|
||||
PR_Lock(ts.ts_s_trim_mutex);
|
||||
max_age = ts.ts_c_max_age;
|
||||
trim_interval = ts.ts_c_trim_interval;
|
||||
@@ -257,7 +259,7 @@ trim_changelog(void)
|
||||
*/
|
||||
done = 0;
|
||||
now_maxage = slapi_current_utc_time(); /* real time for trim candidates */
|
||||
- while (!done && retrocl_trimming == 1) {
|
||||
+ while (!done && retrocl_trimming == 1 && !slapi_is_shutting_down()) {
|
||||
int did_delete;
|
||||
|
||||
did_delete = 0;
|
||||
@@ -309,6 +311,9 @@ trim_changelog(void)
|
||||
"trim_changelog: removed %d change records\n",
|
||||
num_deleted);
|
||||
}
|
||||
+
|
||||
+ g_decr_active_threadcnt();
|
||||
+
|
||||
return rc;
|
||||
}
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,257 @@
|
||||
From 0e98684fb9a4db555b92d5fd2c261f26e89dc16f Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Mon, 2 Mar 2026 14:31:29 -0500
|
||||
Subject: [PATCH 08/13] Issue 7271 - implement a pre-close plugin function
|
||||
|
||||
Description:
|
||||
|
||||
replication protocol could benefit from being notifioed the shutdown process
|
||||
has started before calling the "close" plugin function. This would allow the
|
||||
replication protocol to wake up and adjust its active thread count to prevent
|
||||
a hang during shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: progier, spichugi, and vashirov(Thanks!!!)
|
||||
---
|
||||
ldap/servers/plugins/replication/repl5_init.c | 20 +++++++++++-----
|
||||
ldap/servers/slapd/daemon.c | 6 ++++-
|
||||
ldap/servers/slapd/pblock.c | 14 ++++++++++-
|
||||
ldap/servers/slapd/plugin.c | 24 ++++++++++++++++++-
|
||||
ldap/servers/slapd/proto-slap.h | 3 ++-
|
||||
ldap/servers/slapd/slap.h | 3 ++-
|
||||
ldap/servers/slapd/slapi-plugin.h | 5 ++--
|
||||
7 files changed, 62 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
|
||||
index 9b6523a2e..dd4870746 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_init.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_init.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -122,6 +122,8 @@ static PRUintn thread_private_agmtname; /* thread private index for logging*/
|
||||
static PRUintn thread_private_cache;
|
||||
static PRUintn thread_primary_csn;
|
||||
|
||||
+static int multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)));
|
||||
+
|
||||
char *
|
||||
get_thread_private_agmtname()
|
||||
{
|
||||
@@ -832,10 +834,6 @@ multimaster_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
int rc = 0; /* OK */
|
||||
|
||||
if (!multimaster_stopped_flag) {
|
||||
- if (!is_ldif_dump) {
|
||||
- /* Shut down replication agreements */
|
||||
- agmtlist_shutdown();
|
||||
- }
|
||||
/* if we are cleaning a ruv, stop */
|
||||
stop_ruv_cleaning();
|
||||
/* unregister backend state change notification */
|
||||
@@ -849,6 +847,16 @@ multimaster_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
return rc;
|
||||
}
|
||||
|
||||
+static int
|
||||
+multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
+{
|
||||
+ if (!multisupplier_stopped_flag) {
|
||||
+ /* Shut down replication agreements which will stop all the
|
||||
+ * replication protocol threads */
|
||||
+ agmtlist_shutdown();
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
PRBool
|
||||
multimaster_started()
|
||||
@@ -896,7 +904,7 @@ replication_multimaster_plugin_init(Slapi_PBlock *pb)
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterdesc);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)multimaster_start);
|
||||
rc = slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN, (void *)multimaster_stop);
|
||||
-
|
||||
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_CLOSE_FN, (void *)multisupplier_pre_stop);
|
||||
/* Register the plugin interfaces we implement */
|
||||
/* preop acquires csn generator handle */
|
||||
rc = slapi_register_plugin("preoperation", 1 /* Enabled */,
|
||||
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
||||
index 2534483c1..c2977aa6f 100644
|
||||
--- a/ldap/servers/slapd/daemon.c
|
||||
+++ b/ldap/servers/slapd/daemon.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2021 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1367,6 +1367,10 @@ slapd_daemon(daemon_ports_t *ports)
|
||||
task_cancel_all();
|
||||
}
|
||||
|
||||
+ /* Call plugin pre close functions */
|
||||
+ plugin_pre_closeall();
|
||||
+
|
||||
+ /* Now wait for active threads to terminate */
|
||||
threads = g_get_active_threadcnt();
|
||||
if (threads > 0) {
|
||||
slapi_log_err(SLAPI_LOG_INFO, "slapd_daemon",
|
||||
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
|
||||
index 7c1cdc81c..b97c1acef 100644
|
||||
--- a/ldap/servers/slapd/pblock.c
|
||||
+++ b/ldap/servers/slapd/pblock.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -2524,6 +2524,14 @@ slapi_pblock_get(Slapi_PBlock *pblock, int arg, void *value)
|
||||
}
|
||||
break;
|
||||
|
||||
+ case SLAPI_PLUGIN_PRE_CLOSE_FN:
|
||||
+ if (pblock->pb_plugin != NULL) {
|
||||
+ (*(IFP *)value) = pblock->pb_plugin->plg_pre_close;
|
||||
+ } else {
|
||||
+ (*(IFP *)value) = NULL;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
default:
|
||||
slapi_log_err(SLAPI_LOG_ERR, "slapi_pblock_get", "Unknown parameter block argument %d\n", arg);
|
||||
PR_ASSERT(0);
|
||||
@@ -4218,6 +4226,10 @@ slapi_pblock_set(Slapi_PBlock *pblock, int arg, void *value)
|
||||
pblock->pb_misc->pb_aci_target_check = *((int *)value);
|
||||
break;
|
||||
|
||||
+ case SLAPI_PLUGIN_PRE_CLOSE_FN:
|
||||
+ pblock->pb_plugin->plg_pre_close = (IFP)value;
|
||||
+ break;
|
||||
+
|
||||
default:
|
||||
slapi_log_err(SLAPI_LOG_ERR, "slapi_pblock_set",
|
||||
"Unknown parameter block argument %d\n", arg);
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 449be2142..8b62dba32 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -1881,6 +1881,28 @@ plugin_dependency_closeall(void)
|
||||
}
|
||||
}
|
||||
|
||||
+/* Call the pre close functions of all the plugins */
|
||||
+void
|
||||
+plugin_pre_closeall(void)
|
||||
+{
|
||||
+ Slapi_PBlock *pb = NULL;
|
||||
+ int plugins_pre_closed = 0;
|
||||
+ int index = 0;
|
||||
+
|
||||
+ while (plugins_pre_closed < global_plugins_started) {
|
||||
+ if (global_plugin_shutdown_order[index].name) {
|
||||
+ if (!global_plugin_shutdown_order[index].removed) {
|
||||
+ pb = slapi_pblock_new();
|
||||
+ plugin_call_one(global_plugin_shutdown_order[index].plugin,
|
||||
+ SLAPI_PLUGIN_PRE_CLOSE_FN, pb);
|
||||
+ slapi_pblock_destroy(pb);
|
||||
+ }
|
||||
+ plugins_pre_closed++;
|
||||
+ }
|
||||
+ index++;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void
|
||||
plugin_freeall(void)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index 6e473a08e..ab42c0e0f 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2005 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* License: GPL (version 3 or any later version).
|
||||
@@ -959,6 +959,7 @@ int plugin_call_exop_plugins(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
Slapi_Backend *plugin_extended_op_getbackend(Slapi_PBlock *pb, struct slapdplugin *p);
|
||||
const char *plugin_extended_op_oid2string(const char *oid);
|
||||
void plugin_closeall(int close_backends, int close_globals);
|
||||
+void plugin_pre_closeall(void);
|
||||
void plugin_dependency_freeall(void);
|
||||
void plugin_startall(int argc, char **argv, char **plugin_list);
|
||||
void plugin_get_plugin_dependencies(char *plugin_name, char ***names);
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index e6b49d366..0f67dda7d 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/** BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2009 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -1009,6 +1009,7 @@ struct slapdplugin
|
||||
char *plg_libpath; /* library path for dll/so */
|
||||
char *plg_initfunc; /* init symbol */
|
||||
IFP plg_close; /* close function */
|
||||
+ IFP plg_pre_close; /* pre close function */
|
||||
Slapi_PluginDesc plg_desc; /* vendor's info */
|
||||
char *plg_name; /* used for plugin rdn in cn=config */
|
||||
struct slapdplugin *plg_next; /* for plugin lists */
|
||||
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
||||
index 6517665a9..5dd78004c 100644
|
||||
--- a/ldap/servers/slapd/slapi-plugin.h
|
||||
+++ b/ldap/servers/slapd/slapi-plugin.h
|
||||
@@ -1,6 +1,6 @@
|
||||
/* BEGIN COPYRIGHT BLOCK
|
||||
* Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
|
||||
- * Copyright (C) 2009 Red Hat, Inc.
|
||||
+ * Copyright (C) 2026 Red Hat, Inc.
|
||||
* Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -6789,7 +6789,7 @@ time_t slapi_current_time(void) __attribute__((deprecated));
|
||||
* and should NOT be used for timer information.
|
||||
*/
|
||||
int32_t slapi_clock_gettime(struct timespec *tp);
|
||||
-/*
|
||||
+/*
|
||||
* slapi_clock_gettime should have better been called
|
||||
* slapi_clock_utc_gettime but sice the function pre-existed
|
||||
* we are just adding an alias (to avoid risking to break
|
||||
@@ -7093,6 +7093,7 @@ typedef struct slapi_plugindesc
|
||||
|
||||
/* miscellaneous plugin functions */
|
||||
#define SLAPI_PLUGIN_CLOSE_FN 210
|
||||
+#define SLAPI_PLUGIN_PRE_CLOSE_FN 211
|
||||
#define SLAPI_PLUGIN_START_FN 212
|
||||
#define SLAPI_PLUGIN_CLEANUP_FN 232
|
||||
#define SLAPI_PLUGIN_POSTSTART_FN 233
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
From fbe7a335332c6afc676174a47bf109e2c8cf1647 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 4 Mar 2026 14:47:01 -0500
|
||||
Subject: [PATCH 09/13] Issue 7271 - Add new plugin pre-close function check to
|
||||
plugin_invoke_plugin_pb
|
||||
|
||||
Description:
|
||||
|
||||
In plugin_invoke_plugin_pb we were not checking for the new pre-close function
|
||||
which led to an error in the logs: pb_op is NULL. In a debug build this leads
|
||||
to an assertion error at shutdown.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7271
|
||||
|
||||
Reviewed by: vashirov(Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/plugin.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
|
||||
index 8b62dba32..ab7875e87 100644
|
||||
--- a/ldap/servers/slapd/plugin.c
|
||||
+++ b/ldap/servers/slapd/plugin.c
|
||||
@@ -3674,6 +3674,7 @@ plugin_invoke_plugin_pb(struct slapdplugin *plugin, int operation, Slapi_PBlock
|
||||
if (operation == SLAPI_PLUGIN_START_FN ||
|
||||
operation == SLAPI_PLUGIN_POSTSTART_FN ||
|
||||
operation == SLAPI_PLUGIN_CLOSE_FN ||
|
||||
+ operation == SLAPI_PLUGIN_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_CLEANUP_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_PRE_CLOSE_FN ||
|
||||
operation == SLAPI_PLUGIN_BE_POST_OPEN_FN ||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
38
SOURCES/0097-Issue-7304-retrocl-should-not-cache-DN.patch
Normal file
38
SOURCES/0097-Issue-7304-retrocl-should-not-cache-DN.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From b18b6ced81be921ba9901e96566d412aa8f2cec6 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Wed, 4 Mar 2026 14:28:59 -0500
|
||||
Subject: [PATCH 10/13] Issue 7304 - retrocl should not cache DN
|
||||
|
||||
Description:
|
||||
|
||||
When adding a record to the retro changelog we pass in the flag
|
||||
SLAPI_OP_FLAG_NEVER_CACHE to prevent the entry from being cached,
|
||||
but we still update the DN cache leading to unexpected memory growth.
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7304
|
||||
|
||||
Reviewed by: tbordaz(Thanks!)
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_add.c | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
index 226b846cb..89a3f18f6 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
|
||||
@@ -1394,8 +1394,11 @@ common_return:
|
||||
struct backdn *bdn = dncache_find_id(&inst->inst_dncache,
|
||||
addingentry->ep_id);
|
||||
if (bdn) { /* already in the dncache */
|
||||
+ if (is_remove_from_cache) {
|
||||
+ CACHE_REMOVE(&inst->inst_dncache, bdn);
|
||||
+ }
|
||||
CACHE_RETURN(&inst->inst_dncache, &bdn);
|
||||
- } else { /* not in the dncache yet */
|
||||
+ } else if (!is_remove_from_cache) { /* not in the dncache yet */
|
||||
Slapi_DN *addingsdn =
|
||||
slapi_sdn_dup(slapi_entry_get_sdn(addingentry->ep_entry));
|
||||
if (addingsdn) {
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
From 317360e7704aacfba1b05d3f68ab99be0fa427b4 Mon Sep 17 00:00:00 2001
|
||||
From: James Chapman <jachapma@redhat.com>
|
||||
Date: Tue, 3 Mar 2026 16:04:16 +0000
|
||||
Subject: [PATCH 11/13] Issue 7291 - Crash when configuring a replica with an
|
||||
incorrect nsds5ReplicaRoot (#7292)
|
||||
|
||||
Description:
|
||||
Configuring a replica when the replica_root does not exist, results in
|
||||
a NULL mapping tree node extension, which is deferenced without checking
|
||||
for NULL.
|
||||
|
||||
Fix:
|
||||
Add a NULL check, log an error message, return LDAP_UNWILLING_TO_PERFORM. Added a CI test.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7291
|
||||
|
||||
Reviewed by: @progier389, @mreynolds389, @droideck (Thank you)
|
||||
---
|
||||
.../suites/replication/replica_config_test.py | 41 +++++++++++++++++++
|
||||
.../replication/repl5_replica_config.c | 20 ++++++---
|
||||
2 files changed, 55 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/replication/replica_config_test.py b/dirsrvtests/tests/suites/replication/replica_config_test.py
|
||||
index 570c3877b..1f1cde565 100644
|
||||
--- a/dirsrvtests/tests/suites/replication/replica_config_test.py
|
||||
+++ b/dirsrvtests/tests/suites/replication/replica_config_test.py
|
||||
@@ -17,6 +17,7 @@ from lib389.topologies import topology_st as topo
|
||||
from lib389.replica import Replicas
|
||||
from lib389.agreement import Agreements
|
||||
from lib389.utils import ds_is_older
|
||||
+from lib389 import Entry
|
||||
|
||||
pytestmark = pytest.mark.tier1
|
||||
|
||||
@@ -39,6 +40,7 @@ replica_dict = {'nsDS5ReplicaRoot': 'dc=example,dc=com',
|
||||
'nsDS5ReplicaBindDN': 'cn=u',
|
||||
'cn': 'replica'}
|
||||
|
||||
+
|
||||
agmt_dict = {'cn': 'test_agreement',
|
||||
'nsDS5ReplicaRoot': 'dc=example,dc=com',
|
||||
'nsDS5ReplicaHost': 'localhost.localdomain',
|
||||
@@ -193,6 +195,45 @@ def test_replica_num_modify(topo, attr, too_small, too_big, overflow, notnum, va
|
||||
# Value is valid
|
||||
replica.replace(attr, valid)
|
||||
|
||||
+def test_replica_config_add_nonexistent_replicaroot(topo):
|
||||
+ """Test that adding a replica config entry with a non existent
|
||||
+ nsds5replicaroot is correctly rejected.
|
||||
+
|
||||
+ :id: 8eec4244-8ebb-40e1-b6f7-364b85a1862b
|
||||
+ :parametrized: no
|
||||
+ :setup: standalone instance
|
||||
+ :steps:
|
||||
+ 1. Attempt to add a replica config entry with an invalid nsds5replicaroot
|
||||
+ 2. Verify that no replica entry is created
|
||||
+ 3. Verify the server is still running
|
||||
+ :expectedresults:
|
||||
+ 1. The operation is rejected with UNWILLING_TO_PERFORM
|
||||
+ 2. Success
|
||||
+ 3. Success
|
||||
+ """
|
||||
+ inst = topo.standalone
|
||||
+ dn = "cn=replica,cn=dc\\3Dexample\\2Cdc\\3Dcom,cn=mapping tree,cn=config"
|
||||
+
|
||||
+ entry = Entry((dn, {
|
||||
+ 'objectclass': [
|
||||
+ 'top',
|
||||
+ 'nsds5replica',
|
||||
+ 'extensibleObject'
|
||||
+ ],
|
||||
+ 'cn': 'replica',
|
||||
+ 'nsds5replicaroot': 'ou=idontexist',
|
||||
+ 'nsds5replicaid': '65535',
|
||||
+ 'nsds5replicatype': '2',
|
||||
+ 'nsds5ReplicaBindDN': 'cn=replication manager,cn=config',
|
||||
+ 'nsds5flags': '0',
|
||||
+ }))
|
||||
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM ):
|
||||
+ inst.add_s(entry)
|
||||
+
|
||||
+ with pytest.raises(ldap.NO_SUCH_OBJECT):
|
||||
+ inst.getEntry(dn, ldap.SCOPE_BASE, '(objectClass=*)')
|
||||
+
|
||||
+ assert inst.status()
|
||||
|
||||
@pytest.mark.xfail(reason="Agreement validation current does not work.")
|
||||
@pytest.mark.parametrize("attr, too_small, too_big, overflow, notnum, valid", agmt_attrs)
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
|
||||
index 8cc7423bf..c6d730892 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_replica_config.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
|
||||
@@ -279,10 +279,17 @@ replica_config_add(Slapi_PBlock *pb __attribute__((unused)),
|
||||
replica_add_by_dn(replica_root);
|
||||
|
||||
mtnode_ext = _replica_config_get_mtnode_ext(e);
|
||||
- PR_ASSERT(mtnode_ext);
|
||||
-
|
||||
+ if (mtnode_ext == NULL) {
|
||||
+ if (errortext != NULL) {
|
||||
+ PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, "replica root '%s' does not exist", replica_root);
|
||||
+ }
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "replica_config_add - replica root '%s' does not exist",
|
||||
+ replica_root);
|
||||
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
|
||||
+ goto done;
|
||||
+ }
|
||||
if (mtnode_ext->replica) {
|
||||
- if ( errortext != NULL ) {
|
||||
+ if (errortext != NULL) {
|
||||
PR_snprintf(errortext, SLAPI_DSE_RETURNTEXT_SIZE, MSG_ALREADYCONFIGURED, replica_root);
|
||||
}
|
||||
slapi_log_err(SLAPI_LOG_ERR, repl_plugin_name, "replica_config_add - "MSG_ALREADYCONFIGURED, replica_root);
|
||||
@@ -306,15 +313,16 @@ replica_config_add(Slapi_PBlock *pb __attribute__((unused)),
|
||||
|
||||
/* add replica object to the hash */
|
||||
*returncode = replica_add_by_name(replica_get_name(r), r); /* Increments object refcnt */
|
||||
- /* delete the dn from the dn hash - done with configuration */
|
||||
- replica_delete_by_dn(replica_root);
|
||||
|
||||
done:
|
||||
|
||||
+ /* delete the dn from the dn hash - done with configuration */
|
||||
+ replica_delete_by_dn(replica_root);
|
||||
+
|
||||
PR_Unlock(s_configLock);
|
||||
|
||||
if (*returncode != LDAP_SUCCESS) {
|
||||
- if (mtnode_ext->replica)
|
||||
+ if (mtnode_ext && mtnode_ext->replica)
|
||||
object_release(mtnode_ext->replica);
|
||||
return SLAPI_DSE_CALLBACK_ERROR;
|
||||
} else
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,822 @@
|
||||
From 6ea0a4542e09191fbce053b9aca9dc0db31a744c Mon Sep 17 00:00:00 2001
|
||||
From: Simon Pichugin <spichugi@redhat.com>
|
||||
Date: Thu, 5 Mar 2026 17:09:13 -0800
|
||||
Subject: [PATCH 12/13] Issue 7300 - RFE - Add OS-level thread names to all
|
||||
server threads (#7301)
|
||||
|
||||
Description: Add slapi_set_thread_name() API wrapping pthread_setname_np
|
||||
and apply it to all server threads.
|
||||
Workers appear as worker-0..N, the listener as "listener",
|
||||
replication threads as "repl-prot", "repl-inc-res", etc.
|
||||
|
||||
For functions that serve as both thread entry points and direct calls
|
||||
(db2ldif_skip_all, dbmdb_recno_cache_build), thin wrapper functions
|
||||
are used to keep thread naming separate from the core logic.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/7300
|
||||
|
||||
Reviewed by: @vashirov, @jchapma, @mreynolds389 (Thanks!!!)
|
||||
---
|
||||
dirsrvtests/tests/suites/threads/__init__.py | 0
|
||||
.../suites/threads/thread_naming_test.py | 76 +++++++++++++++++++
|
||||
ldap/servers/plugins/automember/automember.c | 4 +
|
||||
ldap/servers/plugins/cos/cos_cache.c | 1 +
|
||||
ldap/servers/plugins/linkedattrs/fixup_task.c | 1 +
|
||||
ldap/servers/plugins/memberof/memberof.c | 2 +
|
||||
.../plugins/posix-winsync/posix-group-task.c | 5 ++
|
||||
ldap/servers/plugins/referint/referint.c | 1 +
|
||||
ldap/servers/plugins/replication/cl5_api.c | 1 +
|
||||
ldap/servers/plugins/replication/cl5_test.c | 1 +
|
||||
.../plugins/replication/repl5_inc_protocol.c | 1 +
|
||||
.../plugins/replication/repl5_protocol.c | 1 +
|
||||
.../plugins/replication/repl5_replica.c | 1 +
|
||||
.../plugins/replication/repl5_tot_protocol.c | 1 +
|
||||
ldap/servers/plugins/retrocl/retrocl_trim.c | 1 +
|
||||
ldap/servers/plugins/roles/roles_cache.c | 1 +
|
||||
.../plugins/schema_reload/schema_reload.c | 1 +
|
||||
ldap/servers/plugins/sync/sync_persist.c | 11 +--
|
||||
ldap/servers/plugins/syntaxes/validate_task.c | 1 +
|
||||
ldap/servers/plugins/usn/usn_cleanup.c | 1 +
|
||||
ldap/servers/plugins/views/views.c | 1 +
|
||||
.../slapd/back-ldbm/db-bdb/bdb_import.c | 1 +
|
||||
.../back-ldbm/db-bdb/bdb_import_threads.c | 3 +
|
||||
.../slapd/back-ldbm/db-bdb/bdb_layer.c | 7 ++
|
||||
ldap/servers/slapd/connection.c | 3 +
|
||||
ldap/servers/slapd/csngen.c | 13 ++--
|
||||
ldap/servers/slapd/daemon.c | 2 +
|
||||
ldap/servers/slapd/eventq-deprecated.c | 1 +
|
||||
ldap/servers/slapd/eventq.c | 1 +
|
||||
ldap/servers/slapd/house.c | 1 +
|
||||
ldap/servers/slapd/psearch.c | 1 +
|
||||
ldap/servers/slapd/slapi-plugin.h | 3 +
|
||||
ldap/servers/slapd/task.c | 6 ++
|
||||
ldap/servers/slapd/test-plugins/sampletask.c | 5 ++
|
||||
ldap/servers/slapd/thread_data.c | 8 ++
|
||||
ldap/servers/slapd/vattr.c | 1 +
|
||||
36 files changed, 159 insertions(+), 10 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/suites/threads/__init__.py
|
||||
create mode 100644 dirsrvtests/tests/suites/threads/thread_naming_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/threads/__init__.py b/dirsrvtests/tests/suites/threads/__init__.py
|
||||
new file mode 100644
|
||||
index 000000000..e69de29bb
|
||||
diff --git a/dirsrvtests/tests/suites/threads/thread_naming_test.py b/dirsrvtests/tests/suites/threads/thread_naming_test.py
|
||||
new file mode 100644
|
||||
index 000000000..0f2380ac4
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/threads/thread_naming_test.py
|
||||
@@ -0,0 +1,76 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2026 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+#
|
||||
+import logging
|
||||
+import os
|
||||
+import platform
|
||||
+import pytest
|
||||
+
|
||||
+from lib389 import pid_from_file
|
||||
+from lib389.topologies import topology_st
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+logging.getLogger(__name__).setLevel(logging.INFO)
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+
|
||||
+def _get_thread_names(pid):
|
||||
+ """Read thread names from /proc/<pid>/task/*/comm"""
|
||||
+ names = []
|
||||
+ task_dir = f"/proc/{pid}/task"
|
||||
+ if not os.path.isdir(task_dir):
|
||||
+ pytest.skip(f"{task_dir} not available")
|
||||
+ for tid in os.listdir(task_dir):
|
||||
+ comm_path = os.path.join(task_dir, tid, "comm")
|
||||
+ try:
|
||||
+ with open(comm_path, "r") as f:
|
||||
+ names.append(f.read().strip())
|
||||
+ except (FileNotFoundError, PermissionError):
|
||||
+ pass
|
||||
+ return names
|
||||
+
|
||||
+
|
||||
+def test_thread_names_present(topology_st):
|
||||
+ """Test that key threads have been named via pthread_setname_np
|
||||
+
|
||||
+ :id: a95b4eca-e806-48dc-8c91-debbc3ef4053
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Get the server PID
|
||||
+ 2. Read thread names from /proc/{pid}/task/{tid}/comm
|
||||
+ 3. Verify expected thread names are present
|
||||
+ :expectedresults:
|
||||
+ 1. PID is obtained
|
||||
+ 2. Thread names are readable
|
||||
+ 3. Core thread names (listener, worker, event-q,
|
||||
+ housekeep) are found
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ pid = str(pid_from_file(inst.ds_paths.pid_file))
|
||||
+ log.info(f"Server PID: {pid}")
|
||||
+
|
||||
+ thread_names = _get_thread_names(pid)
|
||||
+ log.info(f"Found {len(thread_names)} threads: {sorted(set(thread_names))}")
|
||||
+
|
||||
+ # Verify the listener thread
|
||||
+ assert "listener" in thread_names, \
|
||||
+ f"'listener' thread not found in {thread_names}"
|
||||
+
|
||||
+ # Verify at least one worker thread
|
||||
+ worker_threads = [n for n in thread_names if n.startswith("worker-")]
|
||||
+ assert len(worker_threads) > 0, \
|
||||
+ f"No 'worker-*' threads found in {thread_names}"
|
||||
+
|
||||
+ # Verify event queue thread
|
||||
+ assert "event-q" in thread_names, \
|
||||
+ f"'event-q' thread not found in {thread_names}"
|
||||
+
|
||||
+ # Verify housekeeping thread
|
||||
+ assert "housekeep" in thread_names, \
|
||||
+ f"'housekeep' thread not found in {thread_names}"
|
||||
diff --git a/ldap/servers/plugins/automember/automember.c b/ldap/servers/plugins/automember/automember.c
|
||||
index 1b1da39b3..65404a574 100644
|
||||
--- a/ldap/servers/plugins/automember/automember.c
|
||||
+++ b/ldap/servers/plugins/automember/automember.c
|
||||
@@ -2300,6 +2300,7 @@ automember_task_abort(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __at
|
||||
void
|
||||
automember_task_abort_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("automem-abort");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
|
||||
slapi_task_inc_refcount(task);
|
||||
@@ -2456,6 +2457,7 @@ out:
|
||||
void
|
||||
automember_rebuild_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("automem-rebld");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
struct configEntry *config = NULL;
|
||||
Slapi_PBlock *search_pb = NULL;
|
||||
@@ -2759,6 +2761,7 @@ out:
|
||||
void
|
||||
automember_export_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("automem-exprt");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
Slapi_PBlock *search_pb = NULL;
|
||||
Slapi_Entry **entries = NULL;
|
||||
@@ -2949,6 +2952,7 @@ out:
|
||||
void
|
||||
automember_map_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("automem-map");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
Slapi_Entry *e = NULL;
|
||||
int result = SLAPI_DSE_CALLBACK_OK;
|
||||
diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c
|
||||
index d404ff901..d8ebbbe3d 100644
|
||||
--- a/ldap/servers/plugins/cos/cos_cache.c
|
||||
+++ b/ldap/servers/plugins/cos/cos_cache.c
|
||||
@@ -373,6 +373,7 @@ out:
|
||||
static void
|
||||
cos_cache_wait_on_change(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("cos-cache");
|
||||
slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_wait_on_change thread\n");
|
||||
|
||||
slapi_lock_mutex(stop_lock);
|
||||
diff --git a/ldap/servers/plugins/linkedattrs/fixup_task.c b/ldap/servers/plugins/linkedattrs/fixup_task.c
|
||||
index f47ccf6d1..e69c62d86 100644
|
||||
--- a/ldap/servers/plugins/linkedattrs/fixup_task.c
|
||||
+++ b/ldap/servers/plugins/linkedattrs/fixup_task.c
|
||||
@@ -107,6 +107,7 @@ linked_attrs_fixup_task_destructor(Slapi_Task *task)
|
||||
static void
|
||||
linked_attrs_fixup_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("linked-fix");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
task_data *td = NULL;
|
||||
PRCList *main_config = NULL;
|
||||
diff --git a/ldap/servers/plugins/memberof/memberof.c b/ldap/servers/plugins/memberof/memberof.c
|
||||
index b52bc0331..153b08ca0 100644
|
||||
--- a/ldap/servers/plugins/memberof/memberof.c
|
||||
+++ b/ldap/servers/plugins/memberof/memberof.c
|
||||
@@ -994,6 +994,7 @@ is_memberof_plugin_started(struct slapdplugin **plg_addr)
|
||||
void
|
||||
deferred_thread_func(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("memberof-def");
|
||||
MemberofDeferredList *deferred_list = (MemberofDeferredList *) arg;
|
||||
MemberofDeferredTask *task;
|
||||
struct slapdplugin *plg = NULL;
|
||||
@@ -3862,6 +3863,7 @@ memberof_qsort_compare(const void *a, const void *b)
|
||||
void
|
||||
memberof_fixup_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("memberof-fix");
|
||||
MemberOfConfig configCopy = {0};
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
task_data *td = NULL;
|
||||
diff --git a/ldap/servers/plugins/posix-winsync/posix-group-task.c b/ldap/servers/plugins/posix-winsync/posix-group-task.c
|
||||
index 922c4f743..96f205822 100644
|
||||
--- a/ldap/servers/plugins/posix-winsync/posix-group-task.c
|
||||
+++ b/ldap/servers/plugins/posix-winsync/posix-group-task.c
|
||||
@@ -1,3 +1,7 @@
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
#include <string.h>
|
||||
#include "posix-wsp-ident.h"
|
||||
#include "posix-group-func.h"
|
||||
@@ -463,6 +467,7 @@ bail:
|
||||
static void
|
||||
posix_group_fixup_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("posix-grp-fix");
|
||||
slapi_log_err(SLAPI_LOG_PLUGIN, POSIX_WINSYNC_PLUGIN_NAME,
|
||||
"_task_thread ==>\n");
|
||||
|
||||
diff --git a/ldap/servers/plugins/referint/referint.c b/ldap/servers/plugins/referint/referint.c
|
||||
index c5e259d8d..2dd3f1ed5 100644
|
||||
--- a/ldap/servers/plugins/referint/referint.c
|
||||
+++ b/ldap/servers/plugins/referint/referint.c
|
||||
@@ -1393,6 +1393,7 @@ referint_postop_close(Slapi_PBlock *pb __attribute__((unused)))
|
||||
void
|
||||
referint_thread_func(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("referint");
|
||||
PRFileDesc *prfd = NULL;
|
||||
char *logfilename = NULL;
|
||||
char thisline[MAX_LINE];
|
||||
diff --git a/ldap/servers/plugins/replication/cl5_api.c b/ldap/servers/plugins/replication/cl5_api.c
|
||||
index 21d2f5b8b..87d517dcf 100644
|
||||
--- a/ldap/servers/plugins/replication/cl5_api.c
|
||||
+++ b/ldap/servers/plugins/replication/cl5_api.c
|
||||
@@ -3156,6 +3156,7 @@ do_cl_compact(time_t when, void *arg)
|
||||
static int
|
||||
_cl5TrimMain(void *param __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("cl-trim");
|
||||
time_t timePrev = slapi_current_utc_time();
|
||||
time_t timeCompactPrev = slapi_current_utc_time();
|
||||
time_t timeNow;
|
||||
diff --git a/ldap/servers/plugins/replication/cl5_test.c b/ldap/servers/plugins/replication/cl5_test.c
|
||||
index efb8c543a..c9078eb2e 100644
|
||||
--- a/ldap/servers/plugins/replication/cl5_test.c
|
||||
+++ b/ldap/servers/plugins/replication/cl5_test.c
|
||||
@@ -551,6 +551,7 @@ clearCSNList(CSN ***csnList, int count)
|
||||
static void
|
||||
threadMain(void *data)
|
||||
{
|
||||
+ slapi_set_thread_name("cl5-test");
|
||||
int entryCount = *(int *)data;
|
||||
CSN **csnList;
|
||||
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
||||
index 846951b9e..23e5b474c 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
||||
@@ -237,6 +237,7 @@ repl5_inc_log_operation_failure(int operation_code, int ldap_error, char *ldap_e
|
||||
static void
|
||||
repl5_inc_result_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("repl-inc-res");
|
||||
result_data *rd = (result_data *)param;
|
||||
ConnResult conres = 0;
|
||||
Repl_Connection *conn = rd->prp->conn;
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
index 6cdbcc4f1..f230fb702 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
|
||||
@@ -226,6 +226,7 @@ finished (any) finished
|
||||
static void
|
||||
prot_thread_main(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("repl-prot");
|
||||
Repl_Protocol *rp = (Repl_Protocol *)arg;
|
||||
int done;
|
||||
Repl_Agmt *agmt = NULL;
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c
|
||||
index cec140140..edfbfce3a 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_replica.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_replica.c
|
||||
@@ -3158,6 +3158,7 @@ process_reap_entry(Slapi_Entry *entry, void *cb_data)
|
||||
static void
|
||||
_replica_reap_tombstones(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("tomb-reap");
|
||||
const char *replica_name = (const char *)arg;
|
||||
Slapi_PBlock *pb = NULL;
|
||||
Replica *replica = NULL;
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c
|
||||
index 4b2064912..1ec930116 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_tot_protocol.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c
|
||||
@@ -102,6 +102,7 @@ repl5_tot_log_operation_failure(int ldap_error, char *ldap_error_string, const c
|
||||
static void
|
||||
repl5_tot_result_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("repl-tot-res");
|
||||
callback_data *cb = (callback_data *)param;
|
||||
ConnResult conres = 0;
|
||||
Repl_Connection *conn = cb->prp->conn;
|
||||
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
index 6514fc790..a363c8a30 100644
|
||||
--- a/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
|
||||
@@ -334,6 +334,7 @@ static int retrocl_active_threads;
|
||||
static void
|
||||
changelog_trim_thread_fn(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("retrocl-trim");
|
||||
PR_AtomicIncrement(&retrocl_active_threads);
|
||||
trim_changelog();
|
||||
PR_AtomicDecrement(&retrocl_active_threads);
|
||||
diff --git a/ldap/servers/plugins/roles/roles_cache.c b/ldap/servers/plugins/roles/roles_cache.c
|
||||
index 329b8c888..5030a2d1b 100644
|
||||
--- a/ldap/servers/plugins/roles/roles_cache.c
|
||||
+++ b/ldap/servers/plugins/roles/roles_cache.c
|
||||
@@ -359,6 +359,7 @@ roles_cache_create_suffix(Slapi_DN *sdn)
|
||||
static void
|
||||
roles_cache_wait_on_change(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("roles-cache");
|
||||
roles_cache_def *roles_def = (roles_cache_def *)arg;
|
||||
|
||||
slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_wait_on_change\n");
|
||||
diff --git a/ldap/servers/plugins/schema_reload/schema_reload.c b/ldap/servers/plugins/schema_reload/schema_reload.c
|
||||
index 847323af4..8b0b64bca 100644
|
||||
--- a/ldap/servers/plugins/schema_reload/schema_reload.c
|
||||
+++ b/ldap/servers/plugins/schema_reload/schema_reload.c
|
||||
@@ -125,6 +125,7 @@ typedef struct _task_data
|
||||
static void
|
||||
schemareload_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("schema-reload");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
int rv = 0;
|
||||
int total_work = 2;
|
||||
diff --git a/ldap/servers/plugins/sync/sync_persist.c b/ldap/servers/plugins/sync/sync_persist.c
|
||||
index a78c273cd..fdb41fd23 100644
|
||||
--- a/ldap/servers/plugins/sync/sync_persist.c
|
||||
+++ b/ldap/servers/plugins/sync/sync_persist.c
|
||||
@@ -260,7 +260,7 @@ ignore_op_pl(Slapi_PBlock *pb)
|
||||
* of the completed operation.
|
||||
* When all operations are completed, if the primary operation is successful it
|
||||
* flushes (enqueue) the operations to the sync repl queue(s), else it just free
|
||||
- * the pending list (skipping enqueue).
|
||||
+ * the pending list (skipping enqueue).
|
||||
*/
|
||||
static void
|
||||
sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber_int_t op_tag, char *label)
|
||||
@@ -286,7 +286,7 @@ sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber
|
||||
ignore_op_pl(pb);
|
||||
return;
|
||||
}
|
||||
-
|
||||
+
|
||||
/* Retrieve the result of the operation */
|
||||
if (slapi_op_internal(pb)) {
|
||||
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
|
||||
@@ -351,11 +351,11 @@ sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber
|
||||
}
|
||||
}
|
||||
if (!curr_op) {
|
||||
- slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM, "%s - operation (op=0x%lx, idx_pl=%d) not found on the pendling list\n",
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, SYNC_PLUGIN_SUBSYSTEM, "%s - operation (op=0x%lx, idx_pl=%d) not found on the pendling list\n",
|
||||
label, (ulong) pb_op, ident->idx_pl);
|
||||
PR_ASSERT(curr_op);
|
||||
}
|
||||
-
|
||||
+
|
||||
/* for diagnostic of the pending list, dump its content if it is too long */
|
||||
for (count = 0, curr_op = prim_op; curr_op; count++, curr_op = curr_op->next);
|
||||
if (loglevel_is_set(SLAPI_LOG_PLUGIN) && (count > 10)) {
|
||||
@@ -432,7 +432,7 @@ sync_update_persist_op(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eprev, ber
|
||||
if (enqueue_it) {
|
||||
sync_queue_change(curr_op);
|
||||
}
|
||||
-
|
||||
+
|
||||
/* now free this pending operation */
|
||||
next = curr_op->next;
|
||||
slapi_entry_free(curr_op->entry);
|
||||
@@ -975,6 +975,7 @@ sync_release_connection(Slapi_PBlock *pb, Slapi_Connection *conn, Slapi_Operatio
|
||||
static void
|
||||
sync_send_results(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("sync-send");
|
||||
SyncRequest *req = (SyncRequest *)arg;
|
||||
SyncQueueNode *qnode, *qnodenext;
|
||||
int conn_acq_flag = 0;
|
||||
diff --git a/ldap/servers/plugins/syntaxes/validate_task.c b/ldap/servers/plugins/syntaxes/validate_task.c
|
||||
index a634fd762..49829ab08 100644
|
||||
--- a/ldap/servers/plugins/syntaxes/validate_task.c
|
||||
+++ b/ldap/servers/plugins/syntaxes/validate_task.c
|
||||
@@ -166,6 +166,7 @@ syntax_validate_task_destructor(Slapi_Task *task)
|
||||
static void
|
||||
syntax_validate_task_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("syntax-valid");
|
||||
int rc = 0;
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
task_data *td = NULL;
|
||||
diff --git a/ldap/servers/plugins/usn/usn_cleanup.c b/ldap/servers/plugins/usn/usn_cleanup.c
|
||||
index 7eaf0f88f..47940eabe 100644
|
||||
--- a/ldap/servers/plugins/usn/usn_cleanup.c
|
||||
+++ b/ldap/servers/plugins/usn/usn_cleanup.c
|
||||
@@ -45,6 +45,7 @@ usn_cleanup_close(void)
|
||||
static void
|
||||
usn_cleanup_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("usn-cleanup");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
int rv = 0;
|
||||
int total_work = 2;
|
||||
diff --git a/ldap/servers/plugins/views/views.c b/ldap/servers/plugins/views/views.c
|
||||
index 64e305a3f..ed2ba73e4 100644
|
||||
--- a/ldap/servers/plugins/views/views.c
|
||||
+++ b/ldap/servers/plugins/views/views.c
|
||||
@@ -1797,5 +1797,6 @@ views_cache_backend_state_change(void *handle __attribute__((unused)), char *be_
|
||||
static void
|
||||
views_cache_act_on_change_thread(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("views-cache");
|
||||
views_cache_create();
|
||||
}
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
index 7f484934f..2eede5931 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import.c
|
||||
@@ -2663,6 +2663,7 @@ error:
|
||||
void
|
||||
import_main(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-import");
|
||||
/* For online import tasks increment/decrement the global thread count */
|
||||
g_incr_active_threadcnt();
|
||||
import_main_offline(arg);
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import_threads.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import_threads.c
|
||||
index e433f3db2..350266a02 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import_threads.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_import_threads.c
|
||||
@@ -340,6 +340,7 @@ import_add_created_attrs(Slapi_Entry *e)
|
||||
void
|
||||
import_producer(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-imp-prod");
|
||||
ImportWorkerInfo *info = (ImportWorkerInfo *)param;
|
||||
ImportJob *job = info->job;
|
||||
ID id = job->first_ID, id_filestart = id;
|
||||
@@ -2469,6 +2470,7 @@ foreman_do_entryrdn(ImportJob *job, FifoItem *fi)
|
||||
void
|
||||
import_foreman(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-imp-frmn");
|
||||
ImportWorkerInfo *info = (ImportWorkerInfo *)param;
|
||||
ImportJob *job = info->job;
|
||||
ldbm_instance *inst = job->inst;
|
||||
@@ -2812,6 +2814,7 @@ error:
|
||||
void
|
||||
import_worker(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-imp-work");
|
||||
ImportWorkerInfo *info = (ImportWorkerInfo *)param;
|
||||
ImportJob *job = info->job;
|
||||
ldbm_instance *inst = job->inst;
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
index a2dcc61e1..ec7524d02 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/db-bdb/bdb_layer.c
|
||||
@@ -2909,6 +2909,7 @@ bdb_start_perf_thread(struct ldbminfo *li)
|
||||
static int
|
||||
perf_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-perf");
|
||||
struct ldbminfo *li = NULL;
|
||||
|
||||
PR_ASSERT(NULL != param);
|
||||
@@ -2959,6 +2960,7 @@ bdb_start_locks_monitoring_thread(struct ldbminfo *li)
|
||||
static int
|
||||
locks_monitoring_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-lock-mon");
|
||||
int ret = 0;
|
||||
uint64_t current_locks = 0;
|
||||
uint64_t max_locks = 0;
|
||||
@@ -3169,6 +3171,7 @@ txn_test_init_cfg(txn_test_cfg *cfg)
|
||||
static int
|
||||
txn_test_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-txn-test");
|
||||
struct ldbminfo *li = NULL;
|
||||
Object *inst_obj;
|
||||
int rc = 0;
|
||||
@@ -3467,6 +3470,7 @@ bdb_start_txn_test_thread(struct ldbminfo *li)
|
||||
static int
|
||||
deadlock_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-deadlock");
|
||||
int rval = -1;
|
||||
struct ldbminfo *li = NULL;
|
||||
PRIntervalTime interval; /*NSPR timeout stuffy*/
|
||||
@@ -3564,6 +3568,7 @@ bdb_start_log_flush_thread(struct ldbminfo *li)
|
||||
static int
|
||||
log_flush_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-logflush");
|
||||
PRIntervalTime interval_flush, interval_def;
|
||||
PRIntervalTime last_flush = 0;
|
||||
int i;
|
||||
@@ -3761,6 +3766,7 @@ bdb_start_checkpoint_thread(struct ldbminfo *li)
|
||||
static int
|
||||
checkpoint_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-chkpoint");
|
||||
PRIntervalTime interval;
|
||||
int rval = -1;
|
||||
struct ldbminfo *li = NULL;
|
||||
@@ -3964,6 +3970,7 @@ bdb_start_trickle_thread(struct ldbminfo *li)
|
||||
static int
|
||||
trickle_threadmain(void *param)
|
||||
{
|
||||
+ slapi_set_thread_name("bdb-trickle");
|
||||
PRIntervalTime interval; /*NSPR timeout stuffy*/
|
||||
int rval = -1;
|
||||
dblayer_private *priv = NULL;
|
||||
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
|
||||
index 6c5ef5291..e757c838a 100644
|
||||
--- a/ldap/servers/slapd/connection.c
|
||||
+++ b/ldap/servers/slapd/connection.c
|
||||
@@ -1634,6 +1634,9 @@ connection_threadmain(void *arg)
|
||||
{
|
||||
Slapi_PBlock *pb = slapi_pblock_new();
|
||||
int32_t *snmp_vars_idx = (int32_t *) arg;
|
||||
+ char tname[16];
|
||||
+ snprintf(tname, sizeof(tname), "worker-%d", *snmp_vars_idx);
|
||||
+ slapi_set_thread_name(tname);
|
||||
/* wait forever for new pb until one is available or shutdown */
|
||||
int32_t interval = 0; /* used be 10 seconds */
|
||||
Connection *conn = NULL;
|
||||
diff --git a/ldap/servers/slapd/csngen.c b/ldap/servers/slapd/csngen.c
|
||||
index c7c5c2ba8..8fff8e87e 100644
|
||||
--- a/ldap/servers/slapd/csngen.c
|
||||
+++ b/ldap/servers/slapd/csngen.c
|
||||
@@ -315,15 +315,15 @@ csngen_adjust_time(CSNGen *gen, const CSN *csn)
|
||||
/* To avoid beat phenomena between suppliers let put 1 second in local_offset
|
||||
* it will be eaten at next clock tick rather than increasing remote offset
|
||||
* If we do not do that we will have a time skew drift of 1 second per 2 seconds
|
||||
- * if suppliers are desynchronized by 0.5 second
|
||||
+ * if suppliers are desynchronized by 0.5 second
|
||||
*/
|
||||
if (gen->state.local_offset == 0) {
|
||||
gen->state.local_offset++;
|
||||
gen->state.remote_offset--;
|
||||
}
|
||||
}
|
||||
- /* Time to compute seqnum so that
|
||||
- * new csn >= remote csn and new csn >= old local csn
|
||||
+ /* Time to compute seqnum so that
|
||||
+ * new csn >= remote csn and new csn >= old local csn
|
||||
*/
|
||||
new_time = CSN_CALC_TSTAMP(gen);
|
||||
PR_ASSERT(new_time >= old_time);
|
||||
@@ -570,7 +570,7 @@ _csngen_adjust_local_time(CSNGen *gen)
|
||||
time_t cur_time;
|
||||
int rc;
|
||||
|
||||
-
|
||||
+
|
||||
if ((rc = gen->gettime(&now)) != 0) {
|
||||
/* Failed to get system time, we must abort */
|
||||
slapi_log_err(SLAPI_LOG_ERR, "csngen_new_csn",
|
||||
@@ -617,7 +617,7 @@ _csngen_adjust_local_time(CSNGen *gen)
|
||||
gen->state.sampled_time = cur_time;
|
||||
gen->state.local_offset = MAX_VAL(0, gen->state.local_offset - time_diff);
|
||||
/* new local_offset = MAX_VAL(0, old sample_time + old local_offset - cur_time)
|
||||
- * ==> new local_offset >= 0 and
|
||||
+ * ==> new local_offset >= 0 and
|
||||
* new local_offset + cur_time >= old sample_time + old local_offset
|
||||
* ==> new local_offset + cur_time + remote_offset >=
|
||||
* sample_time + old local_offset + remote_offset
|
||||
@@ -731,6 +731,7 @@ _csngen_stop_test_threads(void)
|
||||
static void
|
||||
_csngen_gen_tester_main(void *data)
|
||||
{
|
||||
+ slapi_set_thread_name("csn-gen-test");
|
||||
CSNGen *gen = (CSNGen *)data;
|
||||
CSN *csn = NULL;
|
||||
char buff[CSN_STRSIZE];
|
||||
@@ -761,6 +762,7 @@ _csngen_gen_tester_main(void *data)
|
||||
static void
|
||||
_csngen_remote_tester_main(void *data)
|
||||
{
|
||||
+ slapi_set_thread_name("csn-rmt-test");
|
||||
CSNGen *gen = (CSNGen *)data;
|
||||
CSN *csn;
|
||||
time_t csn_time;
|
||||
@@ -798,6 +800,7 @@ _csngen_remote_tester_main(void *data)
|
||||
static void
|
||||
_csngen_local_tester_main(void *data)
|
||||
{
|
||||
+ slapi_set_thread_name("csn-lcl-test");
|
||||
CSNGen *gen = (CSNGen *)data;
|
||||
|
||||
PR_ASSERT(gen);
|
||||
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
|
||||
index c2977aa6f..36e52cace 100644
|
||||
--- a/ldap/servers/slapd/daemon.c
|
||||
+++ b/ldap/servers/slapd/daemon.c
|
||||
@@ -426,6 +426,7 @@ disk_mon_check_diskspace(char **dirs, uint64_t threshold, uint64_t *disk_space)
|
||||
void
|
||||
disk_monitoring_thread(void *nothing __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("disk-mon");
|
||||
char **dirs = NULL;
|
||||
char *dirstr = NULL;
|
||||
uint64_t previous_mark = 0;
|
||||
@@ -946,6 +947,7 @@ convert_pbe_des_to_aes(void)
|
||||
void
|
||||
accept_thread(void *vports)
|
||||
{
|
||||
+ slapi_set_thread_name("listener");
|
||||
daemon_ports_t *ports = (daemon_ports_t *)vports;
|
||||
Connection_Table *ct = the_connection_table;
|
||||
PRIntn num_poll = 0;
|
||||
diff --git a/ldap/servers/slapd/eventq-deprecated.c b/ldap/servers/slapd/eventq-deprecated.c
|
||||
index 71a7bf8f5..b9811c905 100644
|
||||
--- a/ldap/servers/slapd/eventq-deprecated.c
|
||||
+++ b/ldap/servers/slapd/eventq-deprecated.c
|
||||
@@ -302,6 +302,7 @@ eq_call_all(void)
|
||||
static void
|
||||
eq_loop(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("event-q-old");
|
||||
while (eq_running) {
|
||||
time_t curtime = slapi_current_utc_time();
|
||||
PRIntervalTime timeout;
|
||||
diff --git a/ldap/servers/slapd/eventq.c b/ldap/servers/slapd/eventq.c
|
||||
index 4c39e08cf..d3a849f66 100644
|
||||
--- a/ldap/servers/slapd/eventq.c
|
||||
+++ b/ldap/servers/slapd/eventq.c
|
||||
@@ -298,6 +298,7 @@ eq_call_all_rel(void)
|
||||
static void
|
||||
eq_loop_rel(void *arg __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("event-q");
|
||||
while (eq_rel_running) {
|
||||
time_t curtime = slapi_current_rel_time_t();
|
||||
int until;
|
||||
diff --git a/ldap/servers/slapd/house.c b/ldap/servers/slapd/house.c
|
||||
index ac1d94f26..78164d5ab 100644
|
||||
--- a/ldap/servers/slapd/house.c
|
||||
+++ b/ldap/servers/slapd/house.c
|
||||
@@ -30,6 +30,7 @@ static pthread_cond_t housekeeping_cvar;
|
||||
static void
|
||||
housecleaning(void *cur_time __attribute__((unused)))
|
||||
{
|
||||
+ slapi_set_thread_name("housekeep");
|
||||
while (!g_get_shutdown()) {
|
||||
struct timespec current_time = {0};
|
||||
/*
|
||||
diff --git a/ldap/servers/slapd/psearch.c b/ldap/servers/slapd/psearch.c
|
||||
index c60e6a8ed..df1dc0162 100644
|
||||
--- a/ldap/servers/slapd/psearch.c
|
||||
+++ b/ldap/servers/slapd/psearch.c
|
||||
@@ -259,6 +259,7 @@ pe_ch_free(PSEQNode **pe)
|
||||
static void
|
||||
ps_send_results(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("ps-send");
|
||||
PSearch *ps = (PSearch *)arg;
|
||||
PSEQNode *peq, *peqnext;
|
||||
struct slapi_filter *filter = 0;
|
||||
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
|
||||
index 5dd78004c..e1cbe5db7 100644
|
||||
--- a/ldap/servers/slapd/slapi-plugin.h
|
||||
+++ b/ldap/servers/slapd/slapi-plugin.h
|
||||
@@ -6109,6 +6109,9 @@ int slapi_wait_condvar(Slapi_CondVar *cvar, struct timeval *timeout) __attribute
|
||||
int slapi_notify_condvar(Slapi_CondVar *cvar, int notify_all);
|
||||
int slapi_wait_condvar_pt(Slapi_CondVar *cvar, Slapi_Mutex *mutex, struct timeval *timeout);
|
||||
|
||||
+/* Set OS-level thread name for visibility in ps, top, perf, etc. (max 15 chars) */
|
||||
+void slapi_set_thread_name(const char *name);
|
||||
+
|
||||
/**
|
||||
* Creates a new read/write lock
|
||||
* If prio_writer the rwlock gives priority on writers
|
||||
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
|
||||
index 1a8be6c85..350a62814 100644
|
||||
--- a/ldap/servers/slapd/task.c
|
||||
+++ b/ldap/servers/slapd/task.c
|
||||
@@ -1113,6 +1113,7 @@ out:
|
||||
static void
|
||||
task_export_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("export");
|
||||
Slapi_PBlock *pb = (Slapi_PBlock *)arg;
|
||||
// I think someone is mis-using this point to store multiple names ...
|
||||
char **instance_names = NULL;
|
||||
@@ -1484,6 +1485,7 @@ out:
|
||||
static void
|
||||
task_backup_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("backup");
|
||||
Slapi_PBlock *pb = (Slapi_PBlock *)arg;
|
||||
Slapi_Task *task = NULL;
|
||||
struct slapdplugin *pb_plugin;
|
||||
@@ -1635,6 +1637,7 @@ out:
|
||||
static void
|
||||
task_restore_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("restore");
|
||||
Slapi_PBlock *pb = (Slapi_PBlock *)arg;
|
||||
Slapi_Task *task = NULL;
|
||||
struct slapdplugin *pb_plugin;
|
||||
@@ -1789,6 +1792,7 @@ out:
|
||||
static void
|
||||
task_index_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("index");
|
||||
Slapi_PBlock *pb = (Slapi_PBlock *)arg;
|
||||
char *instance_name = NULL;
|
||||
char **db2index_attrs = NULL;
|
||||
@@ -2346,6 +2350,7 @@ struct task_tombstone_data
|
||||
static void
|
||||
task_fixup_tombstone_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("tombfix");
|
||||
struct task_tombstone_data *task_data = arg;
|
||||
Slapi_Entry **entries = NULL;
|
||||
Slapi_Task *task = task_data->task;
|
||||
@@ -2956,6 +2961,7 @@ compact_db_task_destructor(Slapi_Task *task)
|
||||
static void
|
||||
task_compact_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("compact");
|
||||
struct task_compact_data *task_data = arg;
|
||||
Slapi_Task *task = task_data->task;
|
||||
Slapi_Backend *be = NULL;
|
||||
diff --git a/ldap/servers/slapd/test-plugins/sampletask.c b/ldap/servers/slapd/test-plugins/sampletask.c
|
||||
index 1734e2904..08c8315a4 100644
|
||||
--- a/ldap/servers/slapd/test-plugins/sampletask.c
|
||||
+++ b/ldap/servers/slapd/test-plugins/sampletask.c
|
||||
@@ -48,6 +48,10 @@
|
||||
* [...] - Sample task finished.
|
||||
*/
|
||||
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
#include "slapi-plugin.h"
|
||||
#include "nspr.h"
|
||||
|
||||
@@ -88,6 +92,7 @@ task_sampletask_start(Slapi_PBlock *pb)
|
||||
static void
|
||||
task_sampletask_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("sample-task");
|
||||
Slapi_Task *task = (Slapi_Task *)arg;
|
||||
char *myarg = NULL;
|
||||
int i, rv = 0;
|
||||
diff --git a/ldap/servers/slapd/thread_data.c b/ldap/servers/slapd/thread_data.c
|
||||
index fb9951d13..1e64a0d77 100644
|
||||
--- a/ldap/servers/slapd/thread_data.c
|
||||
+++ b/ldap/servers/slapd/thread_data.c
|
||||
@@ -60,6 +60,14 @@ slapi_td_init(void)
|
||||
* Wrapper Functions
|
||||
*/
|
||||
|
||||
+/* Set OS-level thread name (visible in ps, top, perf, pstack, etc.)
|
||||
+ * The name is silently truncated to 15 characters by the kernel. */
|
||||
+void
|
||||
+slapi_set_thread_name(const char *name)
|
||||
+{
|
||||
+ pthread_setname_np(pthread_self(), name);
|
||||
+}
|
||||
+
|
||||
/* plugin list locking */
|
||||
int32_t
|
||||
slapi_td_set_plugin_locked()
|
||||
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
|
||||
index 6fad473c4..b722185c2 100644
|
||||
--- a/ldap/servers/slapd/vattr.c
|
||||
+++ b/ldap/servers/slapd/vattr.c
|
||||
@@ -140,6 +140,7 @@ vattr_cleanup()
|
||||
static void
|
||||
vattr_check_thread(void *arg)
|
||||
{
|
||||
+ slapi_set_thread_name("vattr-chk");
|
||||
Slapi_Backend *be = NULL;
|
||||
char *cookie = NULL;
|
||||
Slapi_DN *base_sdn = NULL;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
From dfb686b4d3542c931dcfd4b8ad641a319566d7dc Mon Sep 17 00:00:00 2001
|
||||
From: progier389 <progier@redhat.com>
|
||||
Date: Fri, 17 Nov 2023 12:33:38 +0100
|
||||
Subject: [PATCH 13/13] Issue 5947 - Do not release the target entry in
|
||||
ldbm_back_search_cleanup
|
||||
|
||||
Partial backport from f5bd03748 for 1.4.3.
|
||||
---
|
||||
ldap/servers/slapd/back-ldbm/ldbm_search.c | 10 +++++++++-
|
||||
1 file changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
|
||||
index 27301f453..768151985 100644
|
||||
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
|
||||
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
|
||||
@@ -157,7 +157,9 @@ ldbm_back_search_cleanup(Slapi_PBlock *pb,
|
||||
ldbm_instance *inst;
|
||||
back_search_result_set *sr = NULL;
|
||||
int free_candidates = 1;
|
||||
+ Slapi_Operation *op = NULL;
|
||||
|
||||
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
|
||||
slapi_pblock_get(pb, SLAPI_BACKEND, &be);
|
||||
inst = (ldbm_instance *)be->be_instance_info;
|
||||
/*
|
||||
@@ -165,7 +167,13 @@ ldbm_back_search_cleanup(Slapi_PBlock *pb,
|
||||
* clean it up for the following sessions.
|
||||
*/
|
||||
slapi_be_unset_flag(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
|
||||
- CACHE_RETURN(&inst->inst_cache, &e); /* NULL e is handled correctly */
|
||||
+ if (e != operation_get_target_entry(op)) {
|
||||
+ /*
|
||||
+ * Target entry is released later on
|
||||
+ * (in cache_return_target_entry called by op_shared_search).
|
||||
+ */
|
||||
+ CACHE_RETURN(&inst->inst_cache, &e); /* NULL e is handled correctly */
|
||||
+ }
|
||||
if (inst->inst_ref_count) {
|
||||
slapi_counter_decrement(inst->inst_ref_count);
|
||||
}
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
From b0b0bb3bc096cca0715fa46a7548c4f5a8bf2717 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 18 May 2026 14:16:06 +0200
|
||||
Subject: [PATCH] Issue 7271 - Fix build failure on 1.4.3
|
||||
|
||||
Co-authored-by: James Chapman <jachapma@redhat.com>
|
||||
---
|
||||
ldap/servers/plugins/dna/dna.c | 1 +
|
||||
ldap/servers/plugins/replication/repl5_init.c | 2 +-
|
||||
2 files changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c
|
||||
index ee4132646..e23bfbbae 100644
|
||||
--- a/ldap/servers/plugins/dna/dna.c
|
||||
+++ b/ldap/servers/plugins/dna/dna.c
|
||||
@@ -9,6 +9,7 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
+#include <stdbool.h>
|
||||
|
||||
|
||||
/**
|
||||
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
|
||||
index dd4870746..526c759a5 100644
|
||||
--- a/ldap/servers/plugins/replication/repl5_init.c
|
||||
+++ b/ldap/servers/plugins/replication/repl5_init.c
|
||||
@@ -850,7 +850,7 @@ multimaster_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
static int
|
||||
multisupplier_pre_stop(Slapi_PBlock *pb __attribute__((unused)))
|
||||
{
|
||||
- if (!multisupplier_stopped_flag) {
|
||||
+ if (!multimaster_stopped_flag) {
|
||||
/* Shut down replication agreements which will stop all the
|
||||
* replication protocol threads */
|
||||
agmtlist_shutdown();
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
From 98f67447cf068377efe0a91d8e3c5ef44883fdbc Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Mon, 11 Aug 2025 13:19:13 +0200
|
||||
Subject: [PATCH] Issue 6929 - Compilation failure with rust-1.89 on Fedora ELN
|
||||
|
||||
Bug Description:
|
||||
The `ValueArrayRefIter` struct has a lifetime parameter `'a`.
|
||||
But in the `iter` method the return type doesn't specify the lifetime parameter.
|
||||
|
||||
Fix Description:
|
||||
Make the lifetime explicit.
|
||||
|
||||
Fixes: https://github.com/389ds/389-ds-base/issues/6929
|
||||
|
||||
Reviewed by: @droideck (Thanks!)
|
||||
---
|
||||
src/slapi_r_plugin/src/value.rs | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/slapi_r_plugin/src/value.rs b/src/slapi_r_plugin/src/value.rs
|
||||
index 2fd35c808..fec74ac25 100644
|
||||
--- a/src/slapi_r_plugin/src/value.rs
|
||||
+++ b/src/slapi_r_plugin/src/value.rs
|
||||
@@ -61,7 +61,7 @@ impl ValueArrayRef {
|
||||
ValueArrayRef { raw_slapi_val }
|
||||
}
|
||||
|
||||
- pub fn iter(&self) -> ValueArrayRefIter {
|
||||
+ pub fn iter(&self) -> ValueArrayRefIter<'_> {
|
||||
ValueArrayRefIter {
|
||||
idx: 0,
|
||||
va_ref: &self,
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,439 @@
|
||||
From b7063e97edf005e5e4553faa31c910ddab954257 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Reynolds <mreynolds@redhat.com>
|
||||
Date: Thu, 21 May 2026 09:17:39 -0400
|
||||
Subject: [PATCH] Issue 7503 - CVE-2026-9064 - Add a limit to the number
|
||||
controls per operation
|
||||
|
||||
Description:
|
||||
|
||||
Security fix for CVE-2026-9064
|
||||
|
||||
To prevent resource starvation limit the number of controls the server will
|
||||
process per operation. Reject the operation if number of controls exceeds
|
||||
the limit
|
||||
|
||||
relates: https://github.com/389ds/389-ds-base/issues/7503
|
||||
|
||||
References:
|
||||
- https://access.redhat.com/security/cve/cve-2026-9064
|
||||
- https://bugzilla.redhat.com/show_bug.cgi?id=2480093
|
||||
|
||||
CI test assisted by: Cursor
|
||||
|
||||
Reviewed by: jchapman & tbordaz (Thanks!!)
|
||||
|
||||
(cherry-picked 08a31c002b946973b09e01a5f27214273691fbea)
|
||||
---
|
||||
.../suites/features/ldap_controls_test.py | 157 ++++++++++++++++++
|
||||
ldap/schema/01core389.ldif | 5 +-
|
||||
ldap/servers/slapd/control.c | 14 +-
|
||||
ldap/servers/slapd/libglobs.c | 52 ++++++
|
||||
ldap/servers/slapd/proto-slap.h | 2 +
|
||||
ldap/servers/slapd/slap.h | 4 +
|
||||
.../389-console/src/lib/server/tuning.jsx | 6 +
|
||||
7 files changed, 237 insertions(+), 3 deletions(-)
|
||||
create mode 100644 dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
|
||||
diff --git a/dirsrvtests/tests/suites/features/ldap_controls_test.py b/dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
new file mode 100644
|
||||
index 000000000..59a58b21d
|
||||
--- /dev/null
|
||||
+++ b/dirsrvtests/tests/suites/features/ldap_controls_test.py
|
||||
@@ -0,0 +1,157 @@
|
||||
+# --- BEGIN COPYRIGHT BLOCK ---
|
||||
+# Copyright (C) 2025 Red Hat, Inc.
|
||||
+# All rights reserved.
|
||||
+#
|
||||
+# License: GPL (version 3 or any later version).
|
||||
+# See LICENSE for details.
|
||||
+# --- END COPYRIGHT BLOCK ---
|
||||
+
|
||||
+import logging
|
||||
+import pytest
|
||||
+import ldap
|
||||
+from ldap.controls import RequestControl
|
||||
+from ldap.controls.readentry import PostReadControl
|
||||
+from lib389.idm.user import UserAccounts, UserAccount
|
||||
+from test389.topologies import topology_st
|
||||
+from lib389._constants import DEFAULT_SUFFIX, DN_DM, PASSWORD
|
||||
+
|
||||
+pytestmark = pytest.mark.tier1
|
||||
+
|
||||
+log = logging.getLogger(__name__)
|
||||
+
|
||||
+MANAGE_DSAIT_OID = "2.16.840.1.113730.3.4.2"
|
||||
+MAX_CONTROLS_PER_OP_ATTR = "nsslapd-maxcontrolsperop"
|
||||
+
|
||||
+def test_postread_ctrl_modify(topology_st):
|
||||
+ """Test PostReadControl with LDAP modify operations.
|
||||
+
|
||||
+ :id: 47920dc1-7a9b-4e8d-9f3a-6c5d4e3f2a1b
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Create test user entry with initial description
|
||||
+ 2. Verify initial description value
|
||||
+ 3. Modify description with PostReadControl requesting 'cn' and 'description'
|
||||
+ 4. Verify PostReadControl response contains both requested attributes
|
||||
+ 5. Verify the entry was actually modified in the database
|
||||
+ :expectedresults:
|
||||
+ 1. User entry created successfully
|
||||
+ 2. Initial description matches expected value
|
||||
+ 3. Modify operation with control succeeds
|
||||
+ 4. Control response contains both 'cn' and 'description' attributes
|
||||
+ 5. Database entry reflects the modification
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ INITIAL_DESC = "initial description"
|
||||
+ FINAL_DESC = "final description"
|
||||
+
|
||||
+ log.info("Create test user with initial description")
|
||||
+ users = UserAccounts(inst, DEFAULT_SUFFIX)
|
||||
+ user = users.create(properties={
|
||||
+ 'uid': 'postread_user',
|
||||
+ 'cn': 'postread_user',
|
||||
+ 'sn': 'user',
|
||||
+ 'uidNumber': '1000',
|
||||
+ 'gidNumber': '2000',
|
||||
+ 'homeDirectory': '/home/postread_user',
|
||||
+ 'description': INITIAL_DESC
|
||||
+ })
|
||||
+
|
||||
+ log.info("Verify initial description value")
|
||||
+ assert user.present('description', INITIAL_DESC)
|
||||
+
|
||||
+ log.info("Modify description with PostReadControl")
|
||||
+ pr_ctrl = PostReadControl(criticality=True, attrList=['cn', 'description'])
|
||||
+ msg_id = inst.modify_ext(
|
||||
+ user.dn,
|
||||
+ [(ldap.MOD_REPLACE, 'description', FINAL_DESC.encode('utf-8'))],
|
||||
+ serverctrls=[pr_ctrl]
|
||||
+ )
|
||||
+
|
||||
+ log.info("Get result with response controls")
|
||||
+ _, _, _, resp_ctrls = inst.result3(msg_id)
|
||||
+
|
||||
+ log.info("Verify PostReadControl response is properly encoded")
|
||||
+ assert resp_ctrls, "Server should return PostReadControl"
|
||||
+ assert resp_ctrls[0].dn == user.dn, "Control should return correct DN"
|
||||
+ assert 'description' in resp_ctrls[0].entry, "Control should return description attribute"
|
||||
+ assert 'cn' in resp_ctrls[0].entry, "Control should return cn attribute"
|
||||
+
|
||||
+ log.info("Verify entry was modified with correct value")
|
||||
+ user = UserAccount(inst, user.dn)
|
||||
+ assert user.get_attr_val_utf8('description') == FINAL_DESC
|
||||
+ user.delete()
|
||||
+
|
||||
+
|
||||
+def _make_request_controls(count):
|
||||
+ return [
|
||||
+ RequestControl(controlType=MANAGE_DSAIT_OID, criticality=False)
|
||||
+ for _ in range(count)
|
||||
+ ]
|
||||
+
|
||||
+
|
||||
+def test_bind_excessive_controls(topology_st):
|
||||
+ """Bind request control count is limited by nsslapd-maxcontrolsperop
|
||||
+
|
||||
+ :id: c3888f02-2107-4682-a50a-2189d1436233
|
||||
+ :setup: Standalone instance
|
||||
+ :steps:
|
||||
+ 1. Read nsslapd-maxcontrolsperop from cn=config (default 10)
|
||||
+ 2. Bind with one fewer control than the limit
|
||||
+ 3. Bind with one more control than the limit
|
||||
+ 4. Set nsslapd-maxcontrolsperop to 5
|
||||
+ 5. Bind with 4 controls (new limit minus one)
|
||||
+ 6. Bind with 6 controls (over new limit)
|
||||
+ 7. Restore nsslapd-maxcontrolsperop and re-bind as Directory Manager
|
||||
+ :expectedresults:
|
||||
+ 1. Config value is 10
|
||||
+ 2. Bind succeeds
|
||||
+ 3. Bind fails with ldap.UNWILLING_TO_PERFORM
|
||||
+ 4. Success
|
||||
+ 5. Bind succeeds
|
||||
+ 6. Bind fails with ldap.UNWILLING_TO_PERFORM
|
||||
+ 7. Success
|
||||
+ """
|
||||
+ inst = topology_st.standalone
|
||||
+ original_max = inst.config.get_attr_val_utf8(MAX_CONTROLS_PER_OP_ATTR)
|
||||
+
|
||||
+ try:
|
||||
+ max_controls = int(inst.config.get_attr_val_utf8(MAX_CONTROLS_PER_OP_ATTR))
|
||||
+ assert max_controls == 10
|
||||
+
|
||||
+ log.info("Bind with %d controls (limit %d, limit minus one)",
|
||||
+ max_controls - 1, max_controls)
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD,
|
||||
+ serverctrls=_make_request_controls(max_controls - 1))
|
||||
+
|
||||
+ log.info("Bind with %d controls (limit %d plus one)",
|
||||
+ max_controls + 1, max_controls)
|
||||
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD,
|
||||
+ serverctrls=_make_request_controls(max_controls + 1))
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD)
|
||||
+
|
||||
+ lowered_max = 5
|
||||
+ log.info("Set %s to %d", MAX_CONTROLS_PER_OP_ATTR, lowered_max)
|
||||
+ inst.config.set(MAX_CONTROLS_PER_OP_ATTR, str(lowered_max))
|
||||
+ assert int(inst.config.get_attr_val_utf8(MAX_CONTROLS_PER_OP_ATTR)) == lowered_max
|
||||
+
|
||||
+ log.info("Bind with %d controls (lowered limit %d, limit minus one)",
|
||||
+ lowered_max - 1, lowered_max)
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD,
|
||||
+ serverctrls=_make_request_controls(lowered_max - 1))
|
||||
+
|
||||
+ log.info("Bind with %d controls (lowered limit %d plus one)",
|
||||
+ lowered_max + 1, lowered_max)
|
||||
+ with pytest.raises(ldap.UNWILLING_TO_PERFORM):
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD,
|
||||
+ serverctrls=_make_request_controls(lowered_max + 1))
|
||||
+ finally:
|
||||
+ log.info("Restore %s to %s", MAX_CONTROLS_PER_OP_ATTR, original_max)
|
||||
+ inst.config.set(MAX_CONTROLS_PER_OP_ATTR, original_max)
|
||||
+ inst.simple_bind_s(DN_DM, PASSWORD)
|
||||
+
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ CURRENT_FILE = __file__
|
||||
+ pytest.main(["-s", "-v", CURRENT_FILE])
|
||||
+
|
||||
diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif
|
||||
index 4745a6ca0..d2f9db08f 100644
|
||||
--- a/ldap/schema/01core389.ldif
|
||||
+++ b/ldap/schema/01core389.ldif
|
||||
@@ -5,7 +5,7 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
# License: GPL (version 3 or any later version).
|
||||
-# See LICENSE for details.
|
||||
+# See LICENSE for details.
|
||||
# END COPYRIGHT BLOCK
|
||||
#
|
||||
#
|
||||
@@ -331,6 +331,9 @@ attributeTypes: ( 2.16.840.1.113730.3.1.2390 NAME 'nsds5ReplicaKeepAliveUpdateIn
|
||||
attributeTypes: ( 2.16.840.1.113730.3.1.2391 NAME 'dsEntryDN' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 NO-USER-MODIFICATION SINGLE-VALUE USAGE directoryOperation X-ORIGIN '389 Directory Server' )
|
||||
attributeTypes: ( 2.16.840.1.113730.3.1.2392 NAME 'nsslapd-return-original-entrydn' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
|
||||
attributeTypes: ( 2.16.840.1.113730.3.1.2393 NAME 'nsslapd-auditlog-display-attrs' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
|
||||
+attributeTypes: ( 2.16.840.1.113730.3.1.2398 NAME 'nsslapd-haproxy-trusted-ip' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN '389 Directory Server' )
|
||||
+attributeTypes: ( 2.16.840.1.113730.3.1.2400 NAME 'nsslapd-pwdPBKDF2NumIterations' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Directory Server' )
|
||||
+attributeTypes: ( 2.16.840.1.113730.3.1.2402 NAME 'nsslapd-maxcontrolsperop' DESC '389 Directory Server defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN '389 Directory Server' )
|
||||
#
|
||||
# objectclasses
|
||||
#
|
||||
diff --git a/ldap/servers/slapd/control.c b/ldap/servers/slapd/control.c
|
||||
index 4fd8473be..d9b1db2fc 100644
|
||||
--- a/ldap/servers/slapd/control.c
|
||||
+++ b/ldap/servers/slapd/control.c
|
||||
@@ -175,7 +175,7 @@ get_ldapmessage_controls_ext(
|
||||
ber_tag_t tag;
|
||||
/* ber_len_t is uint, cannot be -1 */
|
||||
ber_len_t len = LBER_ERROR;
|
||||
- int rc, maxcontrols, curcontrols;
|
||||
+ int rc, maxcontrols, curcontrols, maxcontrols_per_op;
|
||||
char *last;
|
||||
int managedsait, pwpolicy_ctrl;
|
||||
Connection *pb_conn = NULL;
|
||||
@@ -251,11 +251,21 @@ get_ldapmessage_controls_ext(
|
||||
return (LDAP_PROTOCOL_ERROR);
|
||||
}
|
||||
|
||||
+ maxcontrols_per_op = config_get_maxcontrolsperop();
|
||||
maxcontrols = curcontrols = 0;
|
||||
for (tag = ber_first_element(ber, &len, &last);
|
||||
tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
|
||||
- tag = ber_next_element(ber, &len, last)) {
|
||||
+ tag = ber_next_element(ber, &len, last))
|
||||
+ {
|
||||
len = -1; /* reset */
|
||||
+ if (curcontrols >= maxcontrols_per_op) {
|
||||
+ slapi_log_err(SLAPI_LOG_ERR, "get_ldapmessage_controls_ext",
|
||||
+ "Too many controls in LDAP request (max %d)\n",
|
||||
+ maxcontrols_per_op);
|
||||
+ rc = LDAP_UNWILLING_TO_PERFORM;
|
||||
+ goto free_and_return;
|
||||
+ }
|
||||
+
|
||||
if (curcontrols >= maxcontrols - 1) {
|
||||
#define CONTROL_GRABSIZE 6
|
||||
maxcontrols += CONTROL_GRABSIZE;
|
||||
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
|
||||
index 99b2c5d8e..d956f6940 100644
|
||||
--- a/ldap/servers/slapd/libglobs.c
|
||||
+++ b/ldap/servers/slapd/libglobs.c
|
||||
@@ -1323,6 +1323,11 @@ static struct config_get_and_set
|
||||
(void **)&global_slapdFrontendConfig.ldapssotoken_ttl,
|
||||
CONFIG_INT, NULL, SLAPD_DEFAULT_LDAPSSOTOKEN_TTL_STR, NULL},
|
||||
#endif
|
||||
+ {CONFIG_MAXCONTROLS_PER_OP_ATTRIBUTE, config_set_maxcontrolsperop,
|
||||
+ NULL, 0,
|
||||
+ (void **)&global_slapdFrontendConfig.maxcontrols_per_op,
|
||||
+ CONFIG_INT, (ConfigGetFunc)config_get_maxcontrolsperop,
|
||||
+ SLAPD_DEFAULT_MAXCONTROLS_PER_OP_STR, NULL},
|
||||
/* End config */
|
||||
};
|
||||
|
||||
@@ -1836,6 +1841,7 @@ FrontendConfig_init(void)
|
||||
init_cn_uses_dn_syntax_in_dns = cfg->cn_uses_dn_syntax_in_dns = LDAP_OFF;
|
||||
init_global_backend_local = LDAP_OFF;
|
||||
cfg->maxsimplepaged_per_conn = SLAPD_DEFAULT_MAXSIMPLEPAGED_PER_CONN;
|
||||
+ cfg->maxcontrols_per_op = SLAPD_DEFAULT_MAXCONTROLS_PER_OP;
|
||||
cfg->maxbersize = SLAPD_DEFAULT_MAXBERSIZE;
|
||||
cfg->logging_backend = slapi_ch_strdup(SLAPD_INIT_LOGGING_BACKEND_INTERNAL);
|
||||
cfg->rootdn = slapi_ch_strdup(SLAPD_DEFAULT_DIRECTORY_MANAGER);
|
||||
@@ -9046,6 +9052,52 @@ config_get_maxsimplepaged_per_conn()
|
||||
return retVal;
|
||||
}
|
||||
|
||||
+int
|
||||
+config_set_maxcontrolsperop(const char *attrname, char *value, char *errorbuf, int apply)
|
||||
+{
|
||||
+ int retVal = LDAP_SUCCESS;
|
||||
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
|
||||
+ long size;
|
||||
+ char *endp;
|
||||
+
|
||||
+ if (config_value_is_null(attrname, value, errorbuf, 0)) {
|
||||
+ return LDAP_OPERATIONS_ERROR;
|
||||
+ }
|
||||
+
|
||||
+ errno = 0;
|
||||
+ size = strtol(value, &endp, 10);
|
||||
+ if (*endp != '\0' || errno == ERANGE || size < 1 || size > 1000) {
|
||||
+ slapi_create_errormsg(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
|
||||
+ "(%s) value (%s) is invalid, must be at least 1 and less than 1000\n",
|
||||
+ attrname, value);
|
||||
+ return LDAP_OPERATIONS_ERROR;
|
||||
+ }
|
||||
+
|
||||
+ if (!apply) {
|
||||
+ return retVal;
|
||||
+ }
|
||||
+
|
||||
+ CFG_LOCK_WRITE(slapdFrontendConfig);
|
||||
+
|
||||
+ slapdFrontendConfig->maxcontrols_per_op = size;
|
||||
+
|
||||
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
|
||||
+ return retVal;
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+config_get_maxcontrolsperop()
|
||||
+{
|
||||
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
|
||||
+ int retVal;
|
||||
+
|
||||
+ retVal = slapdFrontendConfig->maxcontrols_per_op;
|
||||
+ if (retVal == 0) {
|
||||
+ retVal = SLAPD_DEFAULT_MAXCONTROLS_PER_OP;
|
||||
+ }
|
||||
+ return retVal;
|
||||
+}
|
||||
+
|
||||
int32_t
|
||||
config_set_extract_pem(const char *attrname, char *value, char *errorbuf, int apply)
|
||||
{
|
||||
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
|
||||
index ab42c0e0f..2ec7c06b7 100644
|
||||
--- a/ldap/servers/slapd/proto-slap.h
|
||||
+++ b/ldap/servers/slapd/proto-slap.h
|
||||
@@ -410,6 +410,7 @@ int32_t config_set_maxdescriptors(const char *attrname, char *value, char *error
|
||||
int config_set_localuser(const char *attrname, char *value, char *errorbuf, int apply);
|
||||
|
||||
int config_set_maxsimplepaged_per_conn(const char *attrname, char *value, char *errorbuf, int apply);
|
||||
+int config_set_maxcontrolsperop(const char *attrname, char *value, char *errorbuf, int apply);
|
||||
|
||||
int log_external_libs_debug_set_log_fn(void);
|
||||
int log_set_backend(const char *attrname, char *value, int logtype, char *errorbuf, int apply);
|
||||
@@ -609,6 +610,7 @@ int config_get_malloc_mmap_threshold(void);
|
||||
#endif
|
||||
|
||||
int config_get_maxsimplepaged_per_conn(void);
|
||||
+int config_get_maxcontrolsperop(void);
|
||||
int config_get_extract_pem(void);
|
||||
|
||||
int32_t config_get_enable_upgrade_hash(void);
|
||||
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
|
||||
index 0f67dda7d..6296de5af 100644
|
||||
--- a/ldap/servers/slapd/slap.h
|
||||
+++ b/ldap/servers/slapd/slap.h
|
||||
@@ -290,6 +290,8 @@ upgrade_status upgrade_server(void);
|
||||
#define SLAPD_DEFAULT_MAXSIMPLEPAGED_PER_CONN_STR "-1"
|
||||
/* We'd like this number to be prime for the hash into the Connection table */
|
||||
#define SLAPD_DEFAULT_CONNTABLESIZE 64000 /* connection table size */
|
||||
+#define SLAPD_DEFAULT_MAXCONTROLS_PER_OP 10
|
||||
+#define SLAPD_DEFAULT_MAXCONTROLS_PER_OP_STR "10"
|
||||
#define SLAPD_DEFAULT_LDAPSSOTOKEN_TTL 3600
|
||||
#define SLAPD_DEFAULT_LDAPSSOTOKEN_TTL_STR "3600"
|
||||
|
||||
@@ -2318,6 +2320,7 @@ typedef struct _slapdEntryPoints
|
||||
#define CONFIG_CN_USES_DN_SYNTAX_IN_DNS "nsslapd-cn-uses-dn-syntax-in-dns"
|
||||
|
||||
#define CONFIG_MAXSIMPLEPAGED_PER_CONN_ATTRIBUTE "nsslapd-maxsimplepaged-per-conn"
|
||||
+#define CONFIG_MAXCONTROLS_PER_OP_ATTRIBUTE "nsslapd-maxcontrolsperop"
|
||||
#define CONFIG_LOGGING_BACKEND "nsslapd-logging-backend"
|
||||
|
||||
#define CONFIG_EXTRACT_PEM "nsslapd-extract-pemfiles"
|
||||
@@ -2613,6 +2616,7 @@ typedef struct _slapdFrontendConfig
|
||||
slapi_onoff_t cn_uses_dn_syntax_in_dns; /* indicates the cn value in dns has dn syntax */
|
||||
slapi_onoff_t global_backend_lock;
|
||||
slapi_int_t maxsimplepaged_per_conn; /* max simple paged results reqs handled per connection */
|
||||
+ slapi_int_t maxcontrols_per_op; /* max LDAP controls allowed per operation */
|
||||
slapi_onoff_t enable_nunc_stans; /* Despite the removal of NS, we have to leave the value in
|
||||
* case someone was setting it.
|
||||
*/
|
||||
diff --git a/src/cockpit/389-console/src/lib/server/tuning.jsx b/src/cockpit/389-console/src/lib/server/tuning.jsx
|
||||
index a8abe1fcc..d5a8817bc 100644
|
||||
--- a/src/cockpit/389-console/src/lib/server/tuning.jsx
|
||||
+++ b/src/cockpit/389-console/src/lib/server/tuning.jsx
|
||||
@@ -27,6 +27,7 @@ const tuning_attrs = [
|
||||
'nsslapd-connection-nocanon',
|
||||
'nsslapd-enable-turbo-mode',
|
||||
'nsslapd-threadnumber',
|
||||
+ 'nsslapd-maxthreadsperconn',
|
||||
'nsslapd-maxdescriptors',
|
||||
'nsslapd-timelimit',
|
||||
'nsslapd-sizelimit',
|
||||
@@ -38,6 +39,7 @@ const tuning_attrs = [
|
||||
'nsslapd-maxsasliosize',
|
||||
'nsslapd-listen-backlog-size',
|
||||
'nsslapd-max-filter-nest-level',
|
||||
+ 'nsslapd-maxcontrolsperop',
|
||||
'nsslapd-ndn-cache-max-size',
|
||||
];
|
||||
|
||||
@@ -163,6 +165,7 @@ export class ServerTuning extends React.Component {
|
||||
'nsslapd-connection-nocanon': connNoCannon,
|
||||
'nsslapd-enable-turbo-mode': turboMode,
|
||||
'nsslapd-threadnumber': attrs['nsslapd-threadnumber'][0],
|
||||
+ 'nsslapd-maxthreadsperconn': attrs['nsslapd-maxthreadsperconn'][0],
|
||||
'nsslapd-maxdescriptors': attrs['nsslapd-maxdescriptors'][0],
|
||||
'nsslapd-timelimit': attrs['nsslapd-timelimit'][0],
|
||||
'nsslapd-sizelimit': attrs['nsslapd-sizelimit'][0],
|
||||
@@ -174,6 +177,7 @@ export class ServerTuning extends React.Component {
|
||||
'nsslapd-maxsasliosize': attrs['nsslapd-maxsasliosize'][0],
|
||||
'nsslapd-listen-backlog-size': attrs['nsslapd-listen-backlog-size'][0],
|
||||
'nsslapd-max-filter-nest-level': attrs['nsslapd-max-filter-nest-level'][0],
|
||||
+ 'nsslapd-maxcontrolsperop': attrs['nsslapd-maxcontrolsperop'][0],
|
||||
'nsslapd-ndn-cache-max-size': attrs['nsslapd-ndn-cache-max-size'][0],
|
||||
// Record original values
|
||||
'_nsslapd-ndn-cache-enabled': ndnEnabled,
|
||||
@@ -181,6 +185,7 @@ export class ServerTuning extends React.Component {
|
||||
'_nsslapd-connection-nocanon': connNoCannon,
|
||||
'_nsslapd-enable-turbo-mode': turboMode,
|
||||
'_nsslapd-threadnumber': attrs['nsslapd-threadnumber'][0],
|
||||
+ '_nsslapd-maxthreadsperconn': attrs['nsslapd-maxthreadsperconn'][0],
|
||||
'_nsslapd-maxdescriptors': attrs['nsslapd-maxdescriptors'][0],
|
||||
'_nsslapd-timelimit': attrs['nsslapd-timelimit'][0],
|
||||
'_nsslapd-sizelimit': attrs['nsslapd-sizelimit'][0],
|
||||
@@ -192,6 +197,7 @@ export class ServerTuning extends React.Component {
|
||||
'_nsslapd-maxsasliosize': attrs['nsslapd-maxsasliosize'][0],
|
||||
'_nsslapd-listen-backlog-size': attrs['nsslapd-listen-backlog-size'][0],
|
||||
'_nsslapd-max-filter-nest-level': attrs['nsslapd-max-filter-nest-level'][0],
|
||||
+ '_nsslapd-maxcontrolsperop': attrs['nsslapd-maxcontrolsperop'][0],
|
||||
'_nsslapd-ndn-cache-max-size': attrs['nsslapd-ndn-cache-max-size'][0],
|
||||
}, this.props.enableTree());
|
||||
})
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -52,7 +52,7 @@ ExcludeArch: i686
|
||||
Summary: 389 Directory Server (base)
|
||||
Name: 389-ds-base
|
||||
Version: 1.4.3.39
|
||||
Release: %{?relprefix}23%{?prerel}%{?dist}
|
||||
Release: %{?relprefix}24%{?prerel}%{?dist}
|
||||
License: GPL-3.0-or-later WITH GPL-3.0-389-ds-base-exception AND (0BSD OR Apache-2.0 OR MIT) AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT) AND (Apache-2.0 OR BSD-2-Clause OR MIT) AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR LGPL-2.1-or-later OR MIT) AND (Apache-2.0 OR MIT OR Zlib) AND (Apache-2.0 OR MIT) AND (MIT OR Apache-2.0) AND Unicode-3.0 AND (MIT OR Unlicense) AND Apache-2.0 AND BSD-3-Clause AND MIT AND MPL-2.0
|
||||
URL: https://www.port389.org
|
||||
Group: System Environment/Daemons
|
||||
@ -385,7 +385,22 @@ Patch84: 0084-Issue-7223-Use-lexicographical-order-for-ancestorid.patch
|
||||
Patch85: 0085-Issue-7223-Remove-integerOrderingMatch-requirement-f.patch
|
||||
Patch86: 0086-Security-fix-for-CVE-2025-14905.patch
|
||||
Patch87: 0087-Issue-7096-During-replication-online-total-init-the-.patch
|
||||
|
||||
Patch88: 0088-Issue-7366-Memory-leaks-in-syncrepl-plugin-during-pe.patch
|
||||
Patch89: 0089-Issue-7126-WARN-keys2idl-received-NULL-idl-from-inde.patch
|
||||
Patch90: 0090-Issue-7124-2nd-BDB-cursor-race-condition-with-transa.patch
|
||||
Patch91: 0091-Issue-7124-BDB-cursor-race-condition-with-transactio.patch
|
||||
Patch92: 0092-Issue-7380-Internal-op-with-negative-wtime-and-large.patch
|
||||
Patch93: 0093-Issue-7152-ns-slapd-fails-to-shutdown-when-deferred-.patch
|
||||
Patch94: 0094-Issue-7271-plugins-that-create-threads-need-to-updat.patch
|
||||
Patch95: 0095-Issue-7271-implement-a-pre-close-plugin-function.patch
|
||||
Patch96: 0096-Issue-7271-Add-new-plugin-pre-close-function-check-t.patch
|
||||
Patch97: 0097-Issue-7304-retrocl-should-not-cache-DN.patch
|
||||
Patch98: 0098-Issue-7291-Crash-when-configuring-a-replica-with-an-.patch
|
||||
Patch99: 0099-Issue-7300-RFE-Add-OS-level-thread-names-to-all-serv.patch
|
||||
Patch100: 0100-Issue-5947-Do-not-release-the-target-entry-in-ldbm_b.patch
|
||||
Patch101: 0101-Issue-7271-fixed-build-error-in-repl5_init.patch
|
||||
Patch102: 0102-Issue-6929-Compilation-failure-with-rust-1.89.patch
|
||||
Patch103: 0103-Issue-7503-CVE-2026-9064-Add-a-limit-to-the-number-c.patch
|
||||
|
||||
#Patch100: cargo.patch
|
||||
|
||||
@ -1045,6 +1060,20 @@ exit 0
|
||||
%doc README.md
|
||||
|
||||
%changelog
|
||||
* Wed May 13 2026 Arun Bansal <arbansal@redhat.com> - 1.4.3.39-24
|
||||
- Bump version to 1.4.3.39-24
|
||||
- Resolves: RHEL-170278 - Memory leaks in syncrepl plugin during persistent search operations [rhel-8.10.z]
|
||||
- Resolves: RHEL-163375 - WARN - keys2idl - received NULL idl from index_read_ext_allids
|
||||
- Resolves: RHEL-159306 - ns-slapd crash in libdb possible memory corruption [rhel-8.10.z]
|
||||
- Resolves: RHEL-170284 - access log - suspicious wtime optime negative and large values in internal op [rhel-8.10.z]
|
||||
- Resolves: RHEL-170507 - ns-slapd fails to shutdown when deferred memberof update is in progress [rhel-8.10.z]
|
||||
- Resolves: RHEL-170509 - Crash in trim_changelog() during the Retro Changelog trimming [rhel-8.10.z]
|
||||
- Resolves: RHEL-170514 - Possible memory leak when using the Retro Changelog plugin [rhel-8.10.z]
|
||||
- Resolves: RHEL-170512 - Crash in replica_config_add when manually configuring a replica with an incorrect nsds5ReplicaRoot [rhel-8.10.z]
|
||||
- Resolves: RHEL-174523 - [RFE] Add OS-level thread names to all server threads [rhel-8.10.z]
|
||||
- Resolves: RHEL-170483 - test_vlv_recreation_reindex fails on LMDB [rhel-8.10.z]
|
||||
- Resolves: RHEL-178076 - CVE-2026-9064 389-ds:1.4/389-ds-base: unbounded LDAP controls count in get_ldapmessage_controls_ext() causes CPU and heap amplification (remote DoS) [rhel-8.10.z]
|
||||
|
||||
* Wed Mar 04 2026 Arun Bansal <arbansal@redhat.com> - 1.4.3.39-23
|
||||
- Resolves: RHEL-137074 - CVE-2025-14905 389-ds:1.4/389-ds-base: 389-ds-base: Remote Code Execution and Denial of Service via heap buffer overflow [rhel-8.10.z]
|
||||
- Resolves: RHEL-152098 - Scalability issue of replication online initialization with large database [rhel-8.10.z]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user