Hello,

    Fix race condition in addrdataset() during SOA serial update.

    https://fedorahosted.org/bind-dyndb-ldap/ticket/89

--
Petr^2 Spacek
From 5e8bc8f943345d8d92900474905288939958dcd8 Mon Sep 17 00:00:00 2001
From: Petr Spacek <pspa...@redhat.com>
Date: Fri, 7 Sep 2012 13:01:57 +0200
Subject: [PATCH] Fix race condition in addrdataset() during SOA serial
 update.

https://fedorahosted.org/bind-dyndb-ldap/ticket/89

Signed-off-by: Petr Spacek <pspa...@redhat.com>
---
 src/ldap_driver.c | 44 ++++++++++++++++++++++++++++++++++----------
 src/ldap_helper.c |  4 ++--
 2 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/src/ldap_driver.c b/src/ldap_driver.c
index 2cdde30cdad9544d530475f5cf4a0b8275a56f03..3a802238028145d35390f6a8d00f156bfdf8e7a1 100644
--- a/src/ldap_driver.c
+++ b/src/ldap_driver.c
@@ -936,6 +936,7 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 	dns_rdatalist_t diff;
 	isc_result_t result;
 	isc_boolean_t rdatalist_exists = ISC_FALSE;
+	isc_boolean_t soa_simulated_write = ISC_FALSE;
 
 	UNUSED(now);
 	UNUSED(db);
@@ -975,42 +976,65 @@ addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
 			rdatalist_removedups(found_rdlist, new_rdlist,
 					     ISC_FALSE, &diff);
 
-			if ((options & DNS_DBADD_MERGE) != 0)
+			if ((options & DNS_DBADD_MERGE) == 0 &&
+			    (rdatalist_length(&diff) != 0)) {
+				CLEANUP_WITH(DNS_R_NOTEXACT);
+			} else {
 				free_rdatalist(ldapdb->common.mctx, &diff);
-			else if (rdatalist_length(&diff) != 0) {
-				free_rdatalist(ldapdb->common.mctx, &diff);
-				result = DNS_R_NOTEXACT;
-				goto cleanup;
 			}
 		} else {
 			/* Replace existing rdataset */
 			free_rdatalist(ldapdb->common.mctx, found_rdlist);
 		}
 	}
 
-	CHECK(write_to_ldap(&ldapdbnode->owner, ldapdb->ldap_inst, new_rdlist));
+	/* HACK: SOA addition will never fail with DNS_R_UNCHANGED.
+	 * This prevents warning from BIND's diff_apply(), it has too strict
+	 * checks for us.
+	 *
+	 * Reason: There is a race condition between SOA serial update
+	 * from BIND's update_action() and our persistent search watcher, because
+	 * they don't know about each other.
+	 * BIND's update_action() changes data with first addrdataset() call and
+	 * then changes serial with second addrdataset() call.
+	 * It can lead to empty diff if persistent search watcher
+	 * incremented serial in meanwhile.
+	 */
+	if (HEAD(new_rdlist->rdata) == NULL) {
+		if (rdlist->type == dns_rdatatype_soa)
+			soa_simulated_write = ISC_TRUE;
+		else
+			CLEANUP_WITH(DNS_R_UNCHANGED);
+	} else {
+		CHECK(write_to_ldap(&ldapdbnode->owner, ldapdb->ldap_inst, new_rdlist));
+	}
+
 
 	if (addedrdataset != NULL) {
-		result = dns_rdatalist_tordataset(new_rdlist, addedrdataset);
-		/* Use strong condition here, returns only SUCCESS */
-		INSIST(result == ISC_R_SUCCESS);
+		if (soa_simulated_write) {
+			dns_rdataset_clone(rdataset, addedrdataset);
+		} else {
+			result = dns_rdatalist_tordataset(new_rdlist, addedrdataset);
+			/* Use strong condition here, returns only SUCCESS */
+			INSIST(result == ISC_R_SUCCESS);
+		}
 	}
 
 	if (rdatalist_exists) {
 		ISC_LIST_APPENDLIST(found_rdlist->rdata, new_rdlist->rdata,
 				    link);
 		SAFE_MEM_PUT_PTR(ldapdb->common.mctx, new_rdlist);
 	} else
 		APPEND(ldapdbnode->rdatalist, new_rdlist, link);
 
-
 	return ISC_R_SUCCESS;
 
 cleanup:
 	if (new_rdlist != NULL) {
 		free_rdatalist(ldapdb->common.mctx, new_rdlist);
 		SAFE_MEM_PUT_PTR(ldapdb->common.mctx, new_rdlist);
 	}
+	free_rdatalist(ldapdb->common.mctx, &diff);
 
 	return result;
 }
diff --git a/src/ldap_helper.c b/src/ldap_helper.c
index 3241ffe486205fa03a6fd1a0a14edf1245c5c4aa..e636a84b35d0bcdc8573c6e7146f38ee21a42076 100644
--- a/src/ldap_helper.c
+++ b/src/ldap_helper.c
@@ -2973,10 +2973,10 @@ soa_serial_increment(isc_mem_t *mctx, ldap_instance_t *inst,
 
 	/* put the new SOA to inst->cache and compare old and new serials */
 	CHECK(ldap_get_zone_serial(inst, zone_name, &new_serial));
-	INSIST(isc_serial_gt(new_serial, old_serial) == ISC_TRUE);
 
 cleanup:
-	if (result != ISC_R_SUCCESS)
+	if (result != ISC_R_SUCCESS ||
+	    isc_serial_gt(new_serial, old_serial) != ISC_TRUE)
 		log_error("SOA serial number incrementation failed in zone '%s'",
 					str_buf(zone_dn));
 
-- 
1.7.11.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to