From 1b3eadb7ee61ecffd559928eaa0054c8243d8aa3 Mon Sep 17 00:00:00 2001
From: Noriko Hosoi <nhosoi@redhat.com>
Date: Thu, 23 Apr 2015 15:43:40 -0700
Subject: [PATCH] Ticket #48146 - async simple paged results issue; need to
 close a small window for a pr index competed among multiple threads.

Description: If multiple async simple paged results requests come in via one
connection simultaneously, the same slot in the paged results array in the
connection could be shared.  If one of them has to do paging, the search
request object stashed in the paged result array slot could be freed by the
other request if it has the shorter life cycle.

These 3 reqs use the same paged results array slot.
req0: <--------------><----x
page1           page2
req1: <----->
req2:              <------->
frees search result object of req0
---
 ldap/servers/slapd/pagedresults.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/ldap/servers/slapd/pagedresults.c b/ldap/servers/slapd/pagedresults.c
index e61c000..aab15c5 100644
--- a/ldap/servers/slapd/pagedresults.c
+++ b/ldap/servers/slapd/pagedresults.c
@@ -41,6 +41,8 @@
 
 #include "slap.h"
 
+#define PAGEDRESULTS_IN_USE ((Slapi_Backend *)0x1)
+
 /*
  * Parse the value from an LDAPv3 "Simple Paged Results" control.  They look
  * like this:
@@ -119,6 +121,7 @@ pagedresults_parse_control_value( Slapi_PBlock *pb,
         } else {
             for (i = 0; i < conn->c_pagedresults.prl_maxlen; i++) {
                 if (!conn->c_pagedresults.prl_list[i].pr_current_be) {
+                    conn->c_pagedresults.prl_list[i].pr_current_be = PAGEDRESULTS_IN_USE;
                     *index = i;
                     break;
                 }
@@ -267,6 +270,7 @@ pagedresults_free_one( Connection *conn, Operation *op, int index )
             PRLock *prmutex = NULL;
             PagedResults *prp = conn->c_pagedresults.prl_list + index;
             if (prp && prp->pr_current_be &&
+                (PAGEDRESULTS_IN_USE != prp->pr_current_be) &&
                 prp->pr_current_be->be_search_results_release &&
                 prp->pr_search_result_set) {
                 prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
@@ -305,6 +309,7 @@ pagedresults_free_one_msgid_nolock( Connection *conn, ber_int_t msgid )
                 if (conn->c_pagedresults.prl_list[i].pr_msgid == msgid) {
                     PagedResults *prp = conn->c_pagedresults.prl_list + i;
                     if (prp && prp->pr_current_be &&
+                        (PAGEDRESULTS_IN_USE != prp->pr_current_be) &&
                         prp->pr_current_be->be_search_results_release &&
                         prp->pr_search_result_set) {
                         prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
@@ -338,6 +343,11 @@ pagedresults_get_current_be(Connection *conn, int index)
             be = conn->c_pagedresults.prl_list[index].pr_current_be;
         }
         PR_Unlock(conn->c_mutex);
+        if (PAGEDRESULTS_IN_USE == be) {
+            LDAPDebug1Arg(LDAP_DEBUG_ANY,
+                  "pagedresults_get_current_be: idx=%d is in use, but backend is not set.\n", index);
+            be = NULL;
+        }
     }
     LDAPDebug1Arg(LDAP_DEBUG_TRACE,
                   "<-- pagedresults_get_current_be: %p\n", be);
@@ -716,6 +726,7 @@ pagedresults_cleanup(Connection *conn, int needlock)
                 i < conn->c_pagedresults.prl_maxlen; i++) {
         prp = conn->c_pagedresults.prl_list + i;
         if (prp->pr_current_be && prp->pr_search_result_set &&
+            (PAGEDRESULTS_IN_USE != prp->pr_current_be) &&
             prp->pr_current_be->be_search_results_release) {
             prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
             rc = 1;
@@ -764,6 +775,7 @@ pagedresults_cleanup_all(Connection *conn, int needlock)
             PR_DestroyLock(prp->pr_mutex);
         }
         if (prp->pr_current_be && prp->pr_search_result_set &&
+            (PAGEDRESULTS_IN_USE != prp->pr_current_be) &&
             prp->pr_current_be->be_search_results_release) {
             prp->pr_current_be->be_search_results_release(&(prp->pr_search_result_set));
             rc = 1;
-- 
1.9.3

