ldap/servers/slapd/back-ldbm/ldbm_search.c | 8 ++++-
ldap/servers/slapd/opshared.c | 10 +++++++
ldap/servers/slapd/pagedresults.c | 40 +++++++++++++++++++++++++++++
ldap/servers/slapd/proto-slap.h | 2 +
ldap/servers/slapd/slap.h | 3 ++
5 files changed, 61 insertions(+), 2 deletions(-)
New commits:
commit 5d4895c4d6a412476f4b831e1d0654931c3934f9
Author: Rich Megginson <rmeggins(a)redhat.com>
Date: Thu Jan 13 13:44:15 2011 -0700
Bug 666076 - dirsrv crash (1.2.7.5) with multiple simple paged result searches
https://bugzilla.redhat.com/show_bug.cgi?id=666076
Resolves: bug 666076
Bug Description: dirsrv crash (1.2.7.5) with multiple simple paged result searches
Reviewed by: nkinder, nhosoi (Thanks!)
Branch: 389-ds-base-1.2.8
Fix Description: Only allow one simple paged results search per-connection
at a time. The new function pagedresults_check_or_set_processing() will
check the flag in the connection to see if pagedresults processing is in
progress. If so, the function will return True and the search will terminate
with LDAP_UNWILLING_TO_PERFORM (as per section 3 in RFC 2696, a server is
allowed to return unwillingToPerform if there is a limit to the number of
outstanding paged search requests from a given client). The processing
flag will be reset once the search result has been sent to the client.
Since the problem is multiple threads in the same connection accessing
the pagedresults data, the workaround is to just set
nsslapd-maxthreadsperconn: 1
in cn=config in dse.ldif.
Platforms tested: RHEL6 x86_64
Flag Day: no
Doc impact: no
(cherry picked from commit a123b5466aae3eac4193451ac587d90ef1b61815)
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c
b/ldap/servers/slapd/back-ldbm/ldbm_search.c
index 88af80f..2ca416f 100644
--- a/ldap/servers/slapd/back-ldbm/ldbm_search.c
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -1201,6 +1201,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension
)
if ( !use_extension )
{
CACHE_RETURN( &inst->inst_cache, &(sr->sr_entry) );
+ sr->sr_entry = NULL;
}
if(sr->sr_vlventry != NULL && !use_extension )
@@ -1239,7 +1240,6 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension
)
curtime = current_time();
if ( tlimit != -1 && curtime > stoptime )
{
- slapi_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL, NULL, nentries,
urls );
/* in case paged results, clean up the conn */
pagedresults_set_search_result(pb->pb_conn, NULL);
slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
@@ -1250,13 +1250,13 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int
use_extension )
slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
delete_search_result_set( &sr );
rc = SLAPI_FAIL_GENERAL;
+ slapi_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL, NULL, nentries,
urls );
goto bail;
}
/* check lookthrough limit */
if ( llimit != -1 && sr->sr_lookthroughcount >= llimit )
{
- slapi_send_ldap_result( pb, LDAP_ADMINLIMIT_EXCEEDED, NULL, NULL, nentries,
urls );
/* in case paged results, clean up the conn */
pagedresults_set_search_result(pb->pb_conn, NULL);
slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, NULL );
@@ -1267,6 +1267,7 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension
)
slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
delete_search_result_set( &sr );
rc = SLAPI_FAIL_GENERAL;
+ slapi_send_ldap_result( pb, LDAP_ADMINLIMIT_EXCEEDED, NULL, NULL, nentries,
urls );
goto bail;
}
@@ -1456,12 +1457,14 @@ ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int
use_extension )
else
{
CACHE_RETURN ( &inst->inst_cache, &(sr->sr_entry) );
+ sr->sr_entry = NULL;
}
}
else
{
/* Failed the filter test, and this isn't a VLV Search */
CACHE_RETURN( &inst->inst_cache, &(sr->sr_entry) );
+ sr->sr_entry = NULL;
if (LDAP_UNWILLING_TO_PERFORM == filter_test) {
/* Need to catch this error to detect the vattr loop */
slapi_send_ldap_result( pb, filter_test, NULL,
@@ -1544,6 +1547,7 @@ delete_search_result_set( back_search_result_set **sr )
{
idl_free( (*sr)->sr_candidates );
}
+ memset( *sr, 0, sizeof( back_search_result_set ) );
slapi_ch_free( (void**)sr );
}
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
index 1f14341..b3e2c45 100644
--- a/ldap/servers/slapd/opshared.c
+++ b/ldap/servers/slapd/opshared.c
@@ -226,6 +226,7 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
Slapi_Backend *pr_be = NULL;
void *pr_search_result = NULL;
int pr_search_result_count = 0;
+ int pr_reset_processing = 0;
be_list[0] = NULL;
referral_list[0] = NULL;
@@ -396,6 +397,12 @@ op_shared_search (Slapi_PBlock *pb, int send_result)
&pagesize, &curr_search_count);
if (LDAP_SUCCESS == rc) {
unsigned int opnote = SLAPI_OP_NOTE_SIMPLEPAGED;
+ if (pagedresults_check_or_set_processing(pb->pb_conn)) {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM,
+ NULL, "Simple Paged Results Search already in
progress on this connection", 0, NULL);
+ goto free_and_return_nolock;
+ }
+ pr_reset_processing = 1; /* need to reset after we are done with this op
*/
operation->o_flags |= OP_FLAG_PAGED_RESULTS;
pr_be = pagedresults_get_current_be(pb->pb_conn);
pr_search_result = pagedresults_get_search_result(pb->pb_conn);
@@ -880,6 +887,9 @@ free_and_return:
slapi_sdn_done(&sdn);
slapi_ch_free_string(&proxydn);
slapi_ch_free_string(&proxystr);
+ if (pr_reset_processing) {
+ pagedresults_reset_processing(pb->pb_conn);
+ }
}
/* Returns 1 if this processing on this entry is finished
diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
index a1b0333..652d4e1 100644
--- a/ldap/servers/slapd/pagedresults.c
+++ b/ldap/servers/slapd/pagedresults.c
@@ -364,6 +364,7 @@ pagedresults_set_timelimit(Connection *conn, time_t timelimit)
}
/*
+ * must be called with conn->c_mutex held
* return values
* 0: not a simple paged result connection
* 1: simple paged result and successfully abandoned
@@ -385,5 +386,44 @@ pagedresults_cleanup(Connection *conn)
}
conn->c_search_result_count = 0;
conn->c_timelimit = 0;
+ conn->c_flags &= ~CONN_FLAG_PAGEDRESULTS_PROCESSING;
return rc;
}
+
+/*
+ * check to see if this connection is currently processing
+ * a pagedresults search - if it is, return True - if not,
+ * mark that it is processing, and return False
+ */
+int
+pagedresults_check_or_set_processing(Connection *conn)
+{
+ int ret = 0;
+ if (conn) {
+ PR_Lock(conn->c_mutex);
+ ret = conn->c_flags&CONN_FLAG_PAGEDRESULTS_PROCESSING;
+ /* if ret is true, the following doesn't do anything */
+ conn->c_flags |= CONN_FLAG_PAGEDRESULTS_PROCESSING;
+ PR_Unlock(conn->c_mutex);
+ }
+ return ret;
+}
+
+/*
+ * mark the connection as being done with pagedresults
+ * processing - returns True if it was processing,
+ * False otherwise
+ */
+int
+pagedresults_reset_processing(Connection *conn)
+{
+ int ret = 0;
+ if (conn) {
+ PR_Lock(conn->c_mutex);
+ ret = conn->c_flags&CONN_FLAG_PAGEDRESULTS_PROCESSING;
+ /* if ret is false, the following doesn't do anything */
+ conn->c_flags &= ~CONN_FLAG_PAGEDRESULTS_PROCESSING;
+ PR_Unlock(conn->c_mutex);
+ }
+ return ret;
+}
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
index 3d22da5..7bd94d4 100644
--- a/ldap/servers/slapd/proto-slap.h
+++ b/ldap/servers/slapd/proto-slap.h
@@ -1387,6 +1387,8 @@ int pagedresults_get_sort_result_code(Connection *conn);
int pagedresults_set_sort_result_code(Connection *conn, int code);
int pagedresults_set_timelimit(Connection *conn, time_t timelimit);
int pagedresults_cleanup(Connection *conn);
+int pagedresults_check_or_set_processing(Connection *conn);
+int pagedresults_reset_processing(Connection *conn);
/*
* sort.c
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
index 65ee8ce..ee02a26 100644
--- a/ldap/servers/slapd/slap.h
+++ b/ldap/servers/slapd/slap.h
@@ -1409,6 +1409,9 @@ typedef struct conn {
#define CONN_FLAG_PAGEDRESULTS_UNINDEXED 128 /* If the search is unindexed,
* store the info in c_flags
*/
+#define CONN_FLAG_PAGEDRESULTS_PROCESSING 256 /* there is an operation
+ * processing a pagedresults search
+ */
#define CONN_GET_SORT_RESULT_CODE (-1)
#define START_TLS_OID "1.3.6.1.4.1.1466.20037"