Blob Blame Raw
From fc9a206c294fb5ea2401a9365f01ef2565799478 Mon Sep 17 00:00:00 2001
From: William Brown <firstyear@redhat.com>
Date: Thu, 2 Nov 2017 13:32:41 +1000
Subject: [PATCH] Ticket 49436 - double free in COS in some conditions

Bug Description:  virtualattrs and COS have some serious memory
ownership issues. What was happening is that COS with multiple
attributes using the same sp_handle would cause a structure
to be registered twice. During shutdown we would then trigger
a double free in the process.

Fix Description:  Change the behaviour of sp_handles to use a
handle *per* attribute we register to guarantee the assocation
between them.

https://pagure.io/389-ds-base/issue/49436

Author: wibrown

Review by: mreynolds, vashirov (Thanks!)

(cherry pick from commit ee4428a3f5d2d8e37a7107c7dce9d622fc17d41c)
---
 dirsrvtests/tests/suites/cos/indirect_cos_test.py |  43 +-
 ldap/servers/plugins/cos/cos_cache.c              | 723 +++++++++++-----------
 ldap/servers/plugins/roles/roles_cache.c          |  50 +-
 ldap/servers/slapd/vattr.c                        |  34 +-
 4 files changed, 406 insertions(+), 444 deletions(-)

diff --git a/dirsrvtests/tests/suites/cos/indirect_cos_test.py b/dirsrvtests/tests/suites/cos/indirect_cos_test.py
index 1aac6b8ed..452edcdf8 100644
--- a/dirsrvtests/tests/suites/cos/indirect_cos_test.py
+++ b/dirsrvtests/tests/suites/cos/indirect_cos_test.py
@@ -7,6 +7,7 @@ import subprocess
 
 from lib389 import Entry
 from lib389.idm.user import UserAccounts
+from lib389.idm.domain import Domain
 from lib389.topologies import topology_st as topo
 from lib389._constants import (DEFAULT_SUFFIX, DN_DM, PASSWORD, HOST_STANDALONE,
                                SERVERID_STANDALONE, PORT_STANDALONE)
@@ -48,14 +49,8 @@ def check_user(inst):
 def setup_subtree_policy(topo):
     """Set up subtree password policy
     """
-    try:
-        topo.standalone.modify_s("cn=config", [(ldap.MOD_REPLACE,
-                                                'nsslapd-pwpolicy-local',
-                                                'on')])
-    except ldap.LDAPError as e:
-        log.error('Failed to set fine-grained policy: error {}'.format(
-            e.message['desc']))
-        raise e
+
+    topo.standalone.config.set('nsslapd-pwpolicy-local', 'on')
 
     log.info('Create password policy for subtree {}'.format(OU_PEOPLE))
     try:
@@ -68,15 +63,9 @@ def setup_subtree_policy(topo):
             OU_PEOPLE, e.message['desc']))
         raise e
 
-    log.info('Add pwdpolicysubentry attribute to {}'.format(OU_PEOPLE))
-    try:
-        topo.standalone.modify_s(DEFAULT_SUFFIX, [(ldap.MOD_REPLACE,
-                                                   'pwdpolicysubentry',
-                                                   PW_POLICY_CONT_PEOPLE2)])
-    except ldap.LDAPError as e:
-        log.error('Failed to pwdpolicysubentry pw policy '
-                  'policy for {}: error {}'.format(OU_PEOPLE, e.message['desc']))
-        raise e
+    domain = Domain(topo.standalone, DEFAULT_SUFFIX)
+    domain.replace('pwdpolicysubentry', PW_POLICY_CONT_PEOPLE2)
+
     time.sleep(1)
 
 
@@ -116,12 +105,9 @@ def setup(topo, request):
     """
     log.info('Add custom schema...')
     try:
-        ATTR_1 = ("( 1.3.6.1.4.1.409.389.2.189 NAME 'x-department' " +
-                  "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")
-        ATTR_2 = ("( 1.3.6.1.4.1.409.389.2.187 NAME 'x-en-ou' " +
-                  "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")
-        OC = ("( xPerson-oid NAME 'xPerson' DESC '' SUP person STRUCTURAL MAY " +
-              "( x-department $ x-en-ou ) X-ORIGIN 'user defined' )")
+        ATTR_1 = (b"( 1.3.6.1.4.1.409.389.2.189 NAME 'x-department' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")
+        ATTR_2 = (b"( 1.3.6.1.4.1.409.389.2.187 NAME 'x-en-ou' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'user defined' )")
+        OC = (b"( xPerson-oid NAME 'xPerson' DESC '' SUP person STRUCTURAL MAY ( x-department $ x-en-ou ) X-ORIGIN 'user defined' )")
         topo.standalone.modify_s("cn=schema", [(ldap.MOD_ADD, 'attributeTypes', ATTR_1),
                                                (ldap.MOD_ADD, 'attributeTypes', ATTR_2),
                                                (ldap.MOD_ADD, 'objectClasses', OC)])
@@ -142,14 +128,9 @@ def setup(topo, request):
         'homeDirectory': '/home/test_user',
         'seeAlso': 'cn=cosTemplate,dc=example,dc=com'
     }
-    users.create(properties=user_properties)
-    try:
-        topo.standalone.modify_s(TEST_USER_DN, [(ldap.MOD_ADD,
-                                                 'objectclass',
-                                                 'xPerson')])
-    except ldap.LDAPError as e:
-        log.fatal('Failed to add objectclass to user')
-        raise e
+    user = users.create(properties=user_properties)
+
+    user.add('objectClass', 'xPerson')
 
     # Setup COS
     log.info("Setup indirect COS...")
diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c
index 87d48908c..0e93183d2 100644
--- a/ldap/servers/plugins/cos/cos_cache.c
+++ b/ldap/servers/plugins/cos/cos_cache.c
@@ -108,9 +108,6 @@ void * cos_get_plugin_identity(void);
 #define COSTYPE_INDIRECT 3
 #define COS_DEF_ERROR_NO_TEMPLATES -2
 
-/* the global plugin handle */
-static volatile vattr_sp_handle *vattr_handle = NULL;
-
 /* both variables are protected by change_lock */
 static int cos_cache_notify_flag = 0;
 static PRBool cos_cache_at_work = PR_FALSE;
@@ -289,75 +286,61 @@ static Slapi_CondVar *start_cond = NULL;
 */
 int cos_cache_init(void)
 {
-	int ret = 0;
-
-	slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_init\n");
-
-	slapi_vattrcache_cache_none();
-	cache_lock = slapi_new_mutex();
-	change_lock = slapi_new_mutex();
-	stop_lock = slapi_new_mutex();
-	something_changed = slapi_new_condvar(change_lock);
-	keeprunning =1;
-	start_lock = slapi_new_mutex();
-	start_cond = slapi_new_condvar(start_lock);
-	started = 0;
-
-	if (stop_lock == NULL ||
-	    change_lock == NULL ||
-	    cache_lock == NULL ||
-	    stop_lock == NULL ||
-	    start_lock == NULL ||
-	    start_cond == NULL ||
-	    something_changed == NULL)
-	{
-		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
-			   "cos_cache_init - Cannot create mutexes\n" );
-				ret = -1;
-		goto out;
-	}
-
-	/* grab the views interface */
-	if(slapi_apib_get_interface(Views_v1_0_GUID, &views_api))
-	{
-		/* lets be tolerant if views is disabled */
-		views_api = 0;
-	}
+    int ret = 0;
 
-	if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle, 
-	                            cos_cache_vattr_get, 
-	                            cos_cache_vattr_compare, 
-	                            cos_cache_vattr_types) != 0)
-	{
-		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
-		   "cos_cache_init - Cannot register as service provider\n" );
-			ret = -1;
-		goto out;
-	}
+    slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "--> cos_cache_init\n");
+
+    slapi_vattrcache_cache_none();
+    cache_lock = slapi_new_mutex();
+    change_lock = slapi_new_mutex();
+    stop_lock = slapi_new_mutex();
+    something_changed = slapi_new_condvar(change_lock);
+    keeprunning = 1;
+    start_lock = slapi_new_mutex();
+    start_cond = slapi_new_condvar(start_lock);
+    started = 0;
+
+    if (stop_lock == NULL ||
+        change_lock == NULL ||
+        cache_lock == NULL ||
+        stop_lock == NULL ||
+        start_lock == NULL ||
+        start_cond == NULL ||
+        something_changed == NULL) {
+        slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
+                      "cos_cache_init - Cannot create mutexes\n");
+        ret = -1;
+        goto out;
+    }
 
-	if ( PR_CreateThread (PR_USER_THREAD, 
-				cos_cache_wait_on_change, 
-				NULL,
-				PR_PRIORITY_NORMAL, 
-				PR_GLOBAL_THREAD, 
-				PR_UNJOINABLE_THREAD, 
-				SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL )
-	{
-		slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
-			   "cos_cache_init - PR_CreateThread failed\n" );
-		ret = -1;
-		goto out;
-	}
+    /* grab the views interface */
+    if (slapi_apib_get_interface(Views_v1_0_GUID, &views_api)) {
+        /* lets be tolerant if views is disabled */
+        views_api = 0;
+    }
 
-		/* wait for that thread to get started */
-		if (ret == 0) {
-			slapi_lock_mutex(start_lock);
-			while (!started) {
-				while (slapi_wait_condvar(start_cond, NULL) == 0);
-			}
-			slapi_unlock_mutex(start_lock);
-		}
+    if (PR_CreateThread(PR_USER_THREAD,
+                        cos_cache_wait_on_change,
+                        NULL,
+                        PR_PRIORITY_NORMAL,
+                        PR_GLOBAL_THREAD,
+                        PR_UNJOINABLE_THREAD,
+                        SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL) {
+        slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
+                      "cos_cache_init - PR_CreateThread failed\n");
+        ret = -1;
+        goto out;
+    }
 
+    /* wait for that thread to get started */
+    if (ret == 0) {
+        slapi_lock_mutex(start_lock);
+        while (!started) {
+            while (slapi_wait_condvar(start_cond, NULL) == 0)
+                ;
+        }
+        slapi_unlock_mutex(start_lock);
+    }
 
 out:
 	slapi_log_err(SLAPI_LOG_TRACE, COS_PLUGIN_SUBSYSTEM, "<-- cos_cache_init\n");
@@ -752,321 +735,311 @@ struct dn_defs_info {
 static int 
 cos_dn_defs_cb (Slapi_Entry* e, void *callback_data)
 {
-	struct dn_defs_info *info;
-	cosAttrValue **pSneakyVal = 0;
-	cosAttrValue *pObjectclass = 0;
-	cosAttrValue *pCosTargetTree = 0;
-	cosAttrValue *pCosTemplateDn = 0;
-	cosAttrValue *pCosSpecifier = 0;
-	cosAttrValue *pCosAttribute = 0;
-	cosAttrValue *pCosOverrides = 0;
-	cosAttrValue *pCosOperational = 0;
-	cosAttrValue *pCosOpDefault = 0;
-	cosAttrValue *pCosMerge = 0;
-	cosAttrValue *pDn = 0;
-	struct berval **dnVals;
-	int cosType = 0;
-	int valIndex = 0;
-	Slapi_Attr *dnAttr;
-	char *attrType = 0;
-	char *norm_dn = NULL;
-	info=(struct dn_defs_info *)callback_data;
-	
-	cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));
-	if(slapi_entry_first_attr(e, &dnAttr)) {
-		goto bail;
-	}
+    struct dn_defs_info *info;
+    cosAttrValue **pSneakyVal = 0;
+    cosAttrValue *pObjectclass = 0;
+    cosAttrValue *pCosTargetTree = 0;
+    cosAttrValue *pCosTemplateDn = 0;
+    cosAttrValue *pCosSpecifier = 0;
+    cosAttrValue *pCosAttribute = 0;
+    cosAttrValue *pCosOverrides = 0;
+    cosAttrValue *pCosOperational = 0;
+    cosAttrValue *pCosOpDefault = 0;
+    cosAttrValue *pCosMerge = 0;
+    cosAttrValue *pDn = 0;
+    struct berval **dnVals;
+    int cosType = 0;
+    int valIndex = 0;
+    Slapi_Attr *dnAttr;
+    char *attrType = 0;
+    char *norm_dn = NULL;
+    info = (struct dn_defs_info *)callback_data;
+
+    cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));
+    if (slapi_entry_first_attr(e, &dnAttr)) {
+        goto bail;
+    }
 
-	do {
-		attrType = 0;		
-		/* we need to fill in the details of the definition now */
-		slapi_attr_get_type(dnAttr, &attrType);		
-		if(!attrType) {
-			continue;
-		}
-		pSneakyVal = 0;
-		if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"objectclass"))
-			pSneakyVal = &pObjectclass;
-		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTargetTree")){
-			if(pCosTargetTree){
-				norm_dn = slapi_create_dn_string("%s", pCosTargetTree->val);
-				if(norm_dn){
-					slapi_ch_free_string(&pCosTargetTree->val);
-					pCosTargetTree->val = norm_dn;
-				}
-			}
-			pSneakyVal = &pCosTargetTree;
-		} else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTemplateDn"))
-			pSneakyVal = &pCosTemplateDn;
-		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosSpecifier"))
-			pSneakyVal = &pCosSpecifier;
-		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosAttribute"))
-			pSneakyVal = &pCosAttribute;
-		else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosIndirectSpecifier"))
-			pSneakyVal = &pCosSpecifier;			
-		if(!pSneakyVal) {
-			continue;
-		}
-		/* It's a type we're interested in */
-		if(slapi_attr_get_bervals_copy(dnAttr, &dnVals)) {
-			continue;
-		}
-		valIndex = 0;
-		if(!dnVals) {
-			continue;
-		}
-		for (valIndex = 0; dnVals[valIndex]; valIndex++)
-		{
-			if(!dnVals[valIndex]->bv_val) {
-				continue;
-			}
-			/*
-			parse any overide or default values
-			and deal with them
-			*/
-			if(pSneakyVal == &pCosAttribute)
-			{
-				int qualifier_hit = 0;
-				int op_qualifier_hit = 0;
-				int merge_schemes_qualifier_hit = 0;
-				int override_qualifier_hit =0;
-				int default_qualifier_hit = 0;
-				int operational_default_qualifier_hit = 0;
-				do
-				{
-					qualifier_hit = 0;
+    do {
+        attrType = 0;
+        /* we need to fill in the details of the definition now */
+        slapi_attr_get_type(dnAttr, &attrType);
+        if (!attrType) {
+            continue;
+        }
+        pSneakyVal = 0;
+        if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"objectclass"))
+            pSneakyVal = &pObjectclass;
+        else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosTargetTree")) {
+            if (pCosTargetTree) {
+                norm_dn = slapi_create_dn_string("%s", pCosTargetTree->val);
+                if (norm_dn) {
+                    slapi_ch_free_string(&pCosTargetTree->val);
+                    pCosTargetTree->val = norm_dn;
+                }
+            }
+            pSneakyVal = &pCosTargetTree;
+        } else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosTemplateDn"))
+            pSneakyVal = &pCosTemplateDn;
+        else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosSpecifier"))
+            pSneakyVal = &pCosSpecifier;
+        else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosAttribute"))
+            pSneakyVal = &pCosAttribute;
+        else if (!slapi_utf8casecmp((unsigned char *)attrType, (unsigned char *)"cosIndirectSpecifier"))
+            pSneakyVal = &pCosSpecifier;
+        if (!pSneakyVal) {
+            continue;
+        }
+        /* It's a type we're interested in */
+        if (slapi_attr_get_bervals_copy(dnAttr, &dnVals)) {
+            continue;
+        }
+        valIndex = 0;
+        if (!dnVals) {
+            continue;
+        }
+        for (valIndex = 0; dnVals[valIndex]; valIndex++) {
+            if (!dnVals[valIndex]->bv_val) {
+                continue;
+            }
+            /*
+            parse any overide or default values
+            and deal with them
+            */
+            if (pSneakyVal == &pCosAttribute) {
+                int qualifier_hit = 0;
+                int op_qualifier_hit = 0;
+                int merge_schemes_qualifier_hit = 0;
+                int override_qualifier_hit = 0;
+                int default_qualifier_hit = 0;
+                int operational_default_qualifier_hit = 0;
+                do {
+                    qualifier_hit = 0;
+
+                    if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational")) {
+                        /* matched */
+                        op_qualifier_hit = 1;
+                        qualifier_hit = 1;
+                    }
+
+                    if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " merge-schemes")) {
+                        /* matched */
+                        merge_schemes_qualifier_hit = 1;
+                        qualifier_hit = 1;
+                    }
+
+                    if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " override")) {
+                        /* matched */
+                        override_qualifier_hit = 1;
+                        qualifier_hit = 1;
+                    }
+
+                    if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " default")) {
+                        default_qualifier_hit = 1;
+                        qualifier_hit = 1;
+                    }
+
+                    if (cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational-default")) {
+                        operational_default_qualifier_hit = 1;
+                        qualifier_hit = 1;
+                    }
+                } while (qualifier_hit == 1);
+
+                /*
+                * At this point, dnVals[valIndex]->bv_val
+                * is the value of cosAttribute, stripped of
+                * any qualifiers, so add this pure attribute type to
+                * the appropriate lists.
+                */
+
+                if (op_qualifier_hit) {
+                    cos_cache_add_attrval(&pCosOperational,
+                                          dnVals[valIndex]->bv_val);
+                }
+                if (merge_schemes_qualifier_hit) {
+                    cos_cache_add_attrval(&pCosMerge, dnVals[valIndex]->bv_val);
+                }
+                if (override_qualifier_hit) {
+                    cos_cache_add_attrval(&pCosOverrides,
+                                          dnVals[valIndex]->bv_val);
+                }
+                if (default_qualifier_hit) {
+                    /* attr is added below in pSneakyVal, in any case */
+                }
+
+                if (operational_default_qualifier_hit) {
+                    cos_cache_add_attrval(&pCosOpDefault,
+                                          dnVals[valIndex]->bv_val);
+                }
+
+                /*
+                 * Each SP_handle is associated to one and only one vattr.
+                 * We could consider making this a single function rather
+                 * than the double-call.
+                 */
+
+                vattr_sp_handle *vattr_handle = NULL;
+
+                if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,
+                                            cos_cache_vattr_get,
+                                            cos_cache_vattr_compare,
+                                            cos_cache_vattr_types) != 0) {
+                    slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_cache_init - Cannot register as service provider for %s\n", dnVals[valIndex]->bv_val);
+                } else {
+                    slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle, dnVals[valIndex]->bv_val, NULL, NULL);
+                }
+
+            } /* if(attrType is cosAttribute) */
+
+            /*
+             * Add the attributetype to the appropriate
+             * list.
+             */
+            cos_cache_add_attrval(pSneakyVal, dnVals[valIndex]->bv_val);
+
+        } /* for (valIndex = 0; dnVals[valIndex]; valIndex++) */
+
+        ber_bvecfree(dnVals);
+        dnVals = NULL;
+    } while (!slapi_entry_next_attr(e, dnAttr, &dnAttr));
+
+    if (pCosAttribute && (!pCosTargetTree || !pCosTemplateDn)) {
+        /* get the parent of the definition */
+        char *orig = slapi_dn_parent(pDn->val);
+        char *parent = NULL;
+        if (orig) {
+            parent = slapi_create_dn_string("%s", orig);
+            if (!parent) {
+                parent = orig;
+                slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
+                              "cos_dn_defs_cb - "
+                              "Failed to normalize parent dn %s. "
+                              "Adding the pre normalized dn.\n",
+                              parent);
+            }
+            if (!pCosTargetTree) {
+                cos_cache_add_attrval(&pCosTargetTree, parent);
+            }
+            if (!pCosTemplateDn) {
+                cos_cache_add_attrval(&pCosTemplateDn, parent);
+            }
+            if (parent != orig) {
+                slapi_ch_free_string(&parent);
+            }
+            slapi_ch_free_string(&orig);
+        } else {
+            slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
+                          "cos_dn_defs_cb - "
+                          "Failed to get parent dn of cos definition %s.\n",
+                          pDn->val);
+            if (!pCosTemplateDn) {
+                if (!pCosTargetTree) {
+                    slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree and cosTemplateDn are not set.\n");
+                } else {
+                    slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTemplateDn is not set.\n");
+                }
+            } else if (!pCosTargetTree) {
+                slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree is not set.\n");
+            }
+        }
+    }
 
-					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational"))
-					{
-						/* matched */
-						op_qualifier_hit = 1;
-						qualifier_hit = 1;
-					}
-					
-					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " merge-schemes"))
-					{
-						/* matched */
-						merge_schemes_qualifier_hit = 1;
-						qualifier_hit = 1;
-					}
+    /*
+    determine the type of class of service scheme
+    */
 
-					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " override"))
-					{
-						/* matched */
-						override_qualifier_hit = 1;
-						qualifier_hit = 1;
-					}
-					
-					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " default")) {
-						default_qualifier_hit = 1;
-						qualifier_hit = 1;
-					}
+    if (pObjectclass) {
+        if (cos_cache_attrval_exists(pObjectclass, "cosDefinition")) {
+            cosType = COSTYPE_CLASSIC;
+        } else if (cos_cache_attrval_exists(pObjectclass, "cosClassicDefinition")) {
+            cosType = COSTYPE_CLASSIC;
 
-					if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational-default")) {
-						operational_default_qualifier_hit = 1;
-						qualifier_hit = 1;
-					}
-				}
-				while(qualifier_hit == 1);
+        } else if (cos_cache_attrval_exists(pObjectclass, "cosPointerDefinition")) {
+            cosType = COSTYPE_POINTER;
 
-				/*
-				* At this point, dnVals[valIndex]->bv_val
-				* is the value of cosAttribute, stripped of
-				* any qualifiers, so add this pure attribute type to
-				* the appropriate lists.
-				*/
-		
-				if ( op_qualifier_hit ) {
-					cos_cache_add_attrval(&pCosOperational,
-					                      dnVals[valIndex]->bv_val);
-				}
-				if ( merge_schemes_qualifier_hit ) {
-					cos_cache_add_attrval(&pCosMerge, dnVals[valIndex]->bv_val);
-				}
-				if ( override_qualifier_hit ) {
-					cos_cache_add_attrval(&pCosOverrides,
-					                      dnVals[valIndex]->bv_val);
-				}
-				if ( default_qualifier_hit ) {
-					/* attr is added below in pSneakyVal, in any case */
-				}
+        } else if (cos_cache_attrval_exists(pObjectclass, "cosIndirectDefinition")) {
+            cosType = COSTYPE_INDIRECT;
 
-				if ( operational_default_qualifier_hit ) {
-					cos_cache_add_attrval(&pCosOpDefault,
-					                      dnVals[valIndex]->bv_val);
-				}
+        } else
+            cosType = COSTYPE_BADTYPE;
+    }
 
-				slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle,
-				                        dnVals[valIndex]->bv_val, NULL, NULL);
-			} /* if(attrType is cosAttribute) */
+    /*
+    we should now have a full definition,
+    do some sanity checks because we don't
+    want bogus entries in the cache
+    then ship it
+    */
+
+    /* these must exist */
+    if (pDn && pObjectclass &&
+        ((cosType == COSTYPE_CLASSIC &&
+          pCosTemplateDn &&
+          pCosSpecifier &&
+          pCosAttribute) ||
+         (cosType == COSTYPE_POINTER &&
+          pCosTemplateDn &&
+          pCosAttribute) ||
+         (cosType == COSTYPE_INDIRECT &&
+          pCosSpecifier &&
+          pCosAttribute))) {
+        int rc = 0;
+        /*
+    we'll leave the referential integrity stuff
+    up to the referint plug-in and assume all
+    is good - if it's not then we just have a
+    useless definition and we'll nag copiously later.
+        */
+        char *pTmpDn = slapi_ch_strdup(pDn->val); /* because dn gets hosed on error */
+
+        if (!(rc = cos_cache_add_defn(info->pDefs, &pDn, cosType,
+                                      &pCosTargetTree, &pCosTemplateDn,
+                                      &pCosSpecifier, &pCosAttribute,
+                                      &pCosOverrides, &pCosOperational,
+                                      &pCosMerge, &pCosOpDefault))) {
+            info->ret = 0; /* we have succeeded to add the defn*/
+        } else {
+            /*
+             * Failed but we will continue the search for other defs
+             * Don't reset info->ret....it keeps track of any success
+            */
+            if (rc == COS_DEF_ERROR_NO_TEMPLATES) {
+                slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s"
+                                                                   "--no CoS Templates found, which should be added before the CoS Definition.\n",
+                              pTmpDn);
+            } else {
+                slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s\n"
+                                                                   "--error(%d)\n",
+                              pTmpDn, rc);
+            }
+        }
 
-			/*
-			 * Add the attributetype to the appropriate
-			 * list.
-			 */
-			cos_cache_add_attrval(pSneakyVal, dnVals[valIndex]->bv_val);
-			
-		}/* for (valIndex = 0; dnVals[valIndex]; valIndex++) */
-		
-		ber_bvecfree( dnVals );
-		dnVals = NULL;
-	} while(!slapi_entry_next_attr(e, dnAttr, &dnAttr));
-
-	if (pCosAttribute && (!pCosTargetTree || !pCosTemplateDn)) {
-		/* get the parent of the definition */
-		char *orig = slapi_dn_parent(pDn->val);
-		char *parent = NULL;
-		if (orig) {
-			parent = slapi_create_dn_string("%s", orig);
-			if (!parent) {
-				parent = orig;
-				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, 
-				              "cos_dn_defs_cb - "
-				              "Failed to normalize parent dn %s. "
-				              "Adding the pre normalized dn.\n", 
-				              parent);
-			}
-			if (!pCosTargetTree) {
-				cos_cache_add_attrval(&pCosTargetTree, parent);
-			}
-			if (!pCosTemplateDn) {
-				cos_cache_add_attrval(&pCosTemplateDn, parent);
-			}
-			if (parent != orig) {
-				slapi_ch_free_string(&parent);
-			}
-			slapi_ch_free_string(&orig);
-		} else {
-			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM,
-			              "cos_dn_defs_cb - "
-			              "Failed to get parent dn of cos definition %s.\n",
-			              pDn->val);
-			if (!pCosTemplateDn) {
-				if (!pCosTargetTree) {
-					slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree and cosTemplateDn are not set.\n");
-				} else {
-					slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTemplateDn is not set.\n");
-				}
-			} else if (!pCosTargetTree) {
-				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - cosTargetTree is not set.\n");
-			}
-		}
-	}
-	
-	/*
-	determine the type of class of service scheme 
-	*/
-	
-	if(pObjectclass)
-	{
-		if(cos_cache_attrval_exists(pObjectclass, "cosDefinition"))
-		{
-			cosType = COSTYPE_CLASSIC;
-		}
-		else if(cos_cache_attrval_exists(pObjectclass, "cosClassicDefinition"))
-		{
-			cosType = COSTYPE_CLASSIC;
-			
-		}
-		else if(cos_cache_attrval_exists(pObjectclass, "cosPointerDefinition"))
-		{
-			cosType = COSTYPE_POINTER;
-			
-		}
-		else if(cos_cache_attrval_exists(pObjectclass, "cosIndirectDefinition"))
-		{
-			cosType = COSTYPE_INDIRECT;
-			
-		}
-		else
-			cosType = COSTYPE_BADTYPE;
-	}
-	
-	/*	
-	we should now have a full definition, 
-	do some sanity checks because we don't
-	want bogus entries in the cache 
-	then ship it
-	*/
-	
-	/* these must exist */
-	if(pDn && pObjectclass && 
-		(
-		(cosType == COSTYPE_CLASSIC &&
-		pCosTemplateDn && 
-		pCosSpecifier &&   
-		pCosAttribute ) 
-		||
-		(cosType == COSTYPE_POINTER &&
-		pCosTemplateDn && 
-		pCosAttribute ) 
-		||
-		(cosType == COSTYPE_INDIRECT &&
-		pCosSpecifier &&   
-		pCosAttribute ) 
-		)
-		)
-	{
-		int rc = 0;
-	/*
-	we'll leave the referential integrity stuff
-	up to the referint plug-in and assume all
-	is good - if it's not then we just have a
-	useless definition and we'll nag copiously later.
-		*/
-		char *pTmpDn = slapi_ch_strdup(pDn->val); /* because dn gets hosed on error */
-		
-		if(!(rc = cos_cache_add_defn(info->pDefs, &pDn, cosType,
-								&pCosTargetTree, &pCosTemplateDn, 
-								&pCosSpecifier, &pCosAttribute,
-								&pCosOverrides, &pCosOperational,
-								&pCosMerge, &pCosOpDefault))) {
-			info->ret = 0;  /* we have succeeded to add the defn*/
-		} else {
-			/*
-			 * Failed but we will continue the search for other defs
-			 * Don't reset info->ret....it keeps track of any success
-			*/
-			if ( rc == COS_DEF_ERROR_NO_TEMPLATES) {
-				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s"
-					"--no CoS Templates found, which should be added before the CoS Definition.\n",
-					pTmpDn);
-			} else {
-				slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - Skipping CoS Definition %s\n"
-					"--error(%d)\n",
-					pTmpDn, rc);
-			}
-		}
-		
-		slapi_ch_free_string(&pTmpDn);
-	}
-	else
-	{
-	/* 
-	this definition is brain dead - bail
-	if we have a dn use it to report, if not then *really* bad
-	things are going on
-		*/
-		if(pDn)
-		{
-			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "
-				"Incomplete cos definition detected in %s, discarding from cache.\n",pDn->val);
-		}
-		else
-			slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "
-				"Incomplete cos definition detected, no DN to report, discarding from cache.\n");
-		
-		if(pCosTargetTree)
-			cos_cache_del_attrval_list(&pCosTargetTree);
-		if(pCosTemplateDn)
-			cos_cache_del_attrval_list(&pCosTemplateDn);
-		if(pCosSpecifier)
-			cos_cache_del_attrval_list(&pCosSpecifier);
-		if(pCosAttribute)
-			cos_cache_del_attrval_list(&pCosAttribute);
-		if(pDn)
-			cos_cache_del_attrval_list(&pDn);
-	}
+        slapi_ch_free_string(&pTmpDn);
+    } else {
+        /*
+    this definition is brain dead - bail
+    if we have a dn use it to report, if not then *really* bad
+    things are going on
+        */
+        if (pDn) {
+            slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "
+                                                               "Incomplete cos definition detected in %s, discarding from cache.\n",
+                          pDn->val);
+        } else
+            slapi_log_err(SLAPI_LOG_ERR, COS_PLUGIN_SUBSYSTEM, "cos_dn_defs_cb - "
+                                                               "Incomplete cos definition detected, no DN to report, discarding from cache.\n");
+
+        if (pCosTargetTree)
+            cos_cache_del_attrval_list(&pCosTargetTree);
+        if (pCosTemplateDn)
+            cos_cache_del_attrval_list(&pCosTemplateDn);
+        if (pCosSpecifier)
+            cos_cache_del_attrval_list(&pCosSpecifier);
+        if (pCosAttribute)
+            cos_cache_del_attrval_list(&pCosAttribute);
+        if (pDn)
+            cos_cache_del_attrval_list(&pDn);
+    }
 bail:
 	/* we don't keep the objectclasses, so lets free them */
 	if(pObjectclass) {
diff --git a/ldap/servers/plugins/roles/roles_cache.c b/ldap/servers/plugins/roles/roles_cache.c
index 3697eaa97..3e1724963 100644
--- a/ldap/servers/plugins/roles/roles_cache.c
+++ b/ldap/servers/plugins/roles/roles_cache.c
@@ -48,9 +48,6 @@ static char *allUserAttributes[] = {
 /* views scoping */
 static void **views_api;
 
-/* Service provider handler */
-static vattr_sp_handle *vattr_handle = NULL;
-
 /* List of nested roles */
 typedef struct _role_object_nested {
 	Slapi_DN *dn;	/* value of attribute nsroledn in a nested role definition */
@@ -224,13 +221,16 @@ int roles_cache_init()
 
 	/* Register a callback on backends creation|modification|deletion, 
       so that we update the corresponding cache */
-	slapi_register_backend_state_change(NULL, roles_cache_trigger_update_suffix);
-   
-	if ( slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle, 
-									roles_sp_get_value, 
-									roles_sp_compare_value, 
-									roles_sp_list_types) )
-	{
+    slapi_register_backend_state_change(NULL, roles_cache_trigger_update_suffix);
+
+    /* Service provider handler - only used once! and freed by vattr! */
+    vattr_sp_handle *vattr_handle = NULL;
+
+
+    if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,
+                                roles_sp_get_value,
+                                roles_sp_compare_value,
+                                roles_sp_list_types)) {
         slapi_log_err(SLAPI_LOG_ERR, ROLES_PLUGIN_SUBSYSTEM,
                "roles_cache_init - slapi_vattrspi_register failed\n");
 
@@ -648,22 +648,20 @@ void roles_cache_stop()
 
     slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_stop\n");
 
-	/* Go through all the roles list and trigger the associated structure */
-	slapi_rwlock_wrlock(global_lock);
-	current_role = roles_list;
-	while ( current_role )
-	{
-		slapi_lock_mutex(current_role->change_lock);
-		current_role->keeprunning = 0;	
-		next_role = current_role->next;
-		slapi_notify_condvar(current_role->something_changed, 1 );
-		slapi_unlock_mutex(current_role->change_lock);
-
-		current_role = next_role;
-	}
-	slapi_rwlock_unlock(global_lock);
-	slapi_ch_free((void **)&vattr_handle);
-	roles_list = NULL;
+    /* Go through all the roles list and trigger the associated structure */
+    slapi_rwlock_wrlock(global_lock);
+    current_role = roles_list;
+    while (current_role) {
+        slapi_lock_mutex(current_role->change_lock);
+        current_role->keeprunning = 0;
+        next_role = current_role->next;
+        slapi_notify_condvar(current_role->something_changed, 1);
+        slapi_unlock_mutex(current_role->change_lock);
+
+        current_role = next_role;
+    }
+    slapi_rwlock_unlock(global_lock);
+    roles_list = NULL;
 
     slapi_log_err(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_stop\n");
 }
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
index ef4d7f279..84e01cd62 100644
--- a/ldap/servers/slapd/vattr.c
+++ b/ldap/servers/slapd/vattr.c
@@ -1843,8 +1843,15 @@ static int vattr_map_create(void)
 	return 0;
 }
 
-void vattr_map_entry_free(vattr_map_entry *vae) {
-    slapi_ch_free((void **)&(vae->sp_list));
+void
+vattr_map_entry_free(vattr_map_entry *vae)
+{
+    vattr_sp_handle *list_entry = vae->sp_list;
+    while (list_entry != NULL) {
+        vattr_sp_handle *next_entry = list_entry->next;
+        slapi_ch_free((void **)&list_entry);
+        list_entry = next_entry;
+    }
     slapi_ch_free_string(&(vae->type_name));
     slapi_ch_free((void **)&vae);
 }
@@ -2134,16 +2141,9 @@ int slapi_vattr_schema_check_type(Slapi_Entry *e, char *type)
 
 vattr_map_entry *vattr_map_entry_new(char *type_name, vattr_sp_handle *sph, void* hint)
 {
-	vattr_map_entry *result = NULL;
-	vattr_sp_handle *sp_copy = NULL;
-
-	sp_copy = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));
-	sp_copy->sp = sph->sp;
-	sp_copy->hint = hint;
-
-	result = (vattr_map_entry*)slapi_ch_calloc(1, sizeof (vattr_map_entry));
-	result->type_name = slapi_ch_strdup(type_name);
-	result->sp_list = sp_copy;
+    vattr_map_entry *result = (vattr_map_entry *)slapi_ch_calloc(1, sizeof(vattr_map_entry));
+    result->type_name = slapi_ch_strdup(type_name);
+    result->sp_list = sph;
 
 	/* go get schema */
 	result->objectclasses = vattr_map_entry_build_schema(type_name);
@@ -2259,6 +2259,16 @@ we'd need to hold a lock on the read path, which we don't want to do.
 So any SP which relinquishes its need to handle a type needs to continue
 to handle the calls on it, but return nothing */
 /* DBDB need to sort out memory ownership here, it's not quite right */
+/*
+ * This function was inconsistent. We would allocated and "kind of",
+ * copy the sp_handle here for the vattr_map_entry_new path. But we
+ * would "take ownership" for the existing entry and the list addition
+ * path. Instead now, EVERY sp_handle we take, we take ownership of
+ * and the CALLER must allocate a new one each time.
+ *
+ * Better idea, is that regattr should just take the fn pointers
+ * and callers never *see* the sp_handle structure at all.
+ */
 
 int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint)
 {
-- 
2.13.6