From 5f934c8c457ffe906c844ab2313943ae9b66bc46 Mon Sep 17 00:00:00 2001
From: Howard Chu <hyc@openldap.org>
Date: Mon, 27 Feb 2023 15:01:23 +0000
Subject: [PATCH] ITS#10016: slapo-syncprov: fix Abandon with active qtask

---
 servers/slapd/overlays/syncprov.c | 42 ++++++++++++++++++++++++++-----
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/servers/slapd/overlays/syncprov.c b/servers/slapd/overlays/syncprov.c
index f62d6c63ea..44a6181410 100644
--- a/servers/slapd/overlays/syncprov.c
+++ b/servers/slapd/overlays/syncprov.c
@@ -871,6 +871,11 @@ static void free_resinfo( syncres *sr )
 
 #define FS_UNLINK	1
 #define FS_LOCK		2
+#define FS_DEFER	4
+
+#define FSR_NOTFREE	0
+#define FSR_DIDFREE	1
+#define FSR_CANFREE	2
 
 static int
 syncprov_free_syncop( syncops *so, int flags )
@@ -881,12 +886,19 @@ syncprov_free_syncop( syncops *so, int flags )
 	if ( flags & FS_LOCK )
 		ldap_pvt_thread_mutex_lock( &so->s_mutex );
 	/* already being freed, or still in use */
-	if ( !so->s_inuse || --so->s_inuse > 0 ) {
+	if ( !so->s_inuse || so->s_inuse > 1 ) {
 		if ( flags & FS_LOCK )
 			ldap_pvt_thread_mutex_unlock( &so->s_mutex );
-		return 0;
+		if ( !( flags & FS_DEFER ) && so->s_inuse )
+			so->s_inuse--;
+		return FSR_NOTFREE;
 	}
 	ldap_pvt_thread_mutex_unlock( &so->s_mutex );
+
+	/* caller wants to cleanup other stuff before actual free */
+	if ( flags & FS_DEFER )
+		return FSR_CANFREE;
+
 	if (( flags & FS_UNLINK ) && so->s_si ) {
 		syncops **sop;
 		ldap_pvt_thread_mutex_lock( &so->s_si->si_ops_mutex );
@@ -914,7 +926,7 @@ syncprov_free_syncop( syncops *so, int flags )
 	}
 	ldap_pvt_thread_mutex_destroy( &so->s_mutex );
 	ch_free( so );
-	return 1;
+	return FSR_DIDFREE;
 }
 
 /* Send a persistent search response */
@@ -1029,6 +1041,9 @@ syncprov_qplay( Operation *op, syncops *so )
 			} else {
 				rc = syncprov_sendresp( op, sr->s_info, so, sr->s_mode );
 			}
+		} else {
+			/* set rc so we don't do a new qstart */
+			rc = 1;
 		}
 
 		free_resinfo( sr );
@@ -1055,6 +1070,9 @@ syncprov_qplay( Operation *op, syncops *so )
 	return rc;
 }
 
+static int
+syncprov_drop_psearch( syncops *so, int lock );
+
 /* task for playing back queued responses */
 static void *
 syncprov_qtask( void *ctx, void *arg )
@@ -1063,7 +1081,7 @@ syncprov_qtask( void *ctx, void *arg )
 	OperationBuffer opbuf;
 	Operation *op;
 	BackendDB be;
-	int rc;
+	int rc, flag, frc;
 
 	op = &opbuf.ob_op;
 	*op = *so->s_op;
@@ -1092,14 +1110,24 @@ syncprov_qtask( void *ctx, void *arg )
 	if ( !rc && !so->s_res )
 		rc = 1;
 
+	flag = FS_UNLINK;
+	if ( rc && op->o_abandon )
+		flag = FS_DEFER;
+
 	/* decrement use count... */
-	if ( !syncprov_free_syncop( so, FS_UNLINK )) {
+	frc = syncprov_free_syncop( so, flag );
+	if ( frc == FSR_NOTFREE ) {
 		if ( rc )
 			/* if we didn't unlink, and task is no longer queued, clear flag */
 			so->s_flags ^= PS_TASK_QUEUED;
 		ldap_pvt_thread_mutex_unlock( &so->s_mutex );
 	}
 
+	/* if we got abandoned while processing, cleanup now */
+	if ( frc == FSR_CANFREE ) {
+		syncprov_drop_psearch( so, 1 );
+	}
+
 	return NULL;
 }
 
@@ -1274,7 +1302,9 @@ syncprov_op_abandon( Operation *op, SlapReply *rs )
 				return SLAP_CB_CONTINUE;
 			}
 		}
-		syncprov_drop_psearch( so, 0 );
+		/* if task is active, it must drop itself */
+		if ( !( so->s_flags & PS_TASK_QUEUED ))
+			syncprov_drop_psearch( so, 0 );
 	}
 	return SLAP_CB_CONTINUE;
 }
-- 
GitLab
