Print this page
NEX-9755 Race in libshare can cause zfs set sharenfs to fail
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>

*** 51,62 **** --- 51,66 ---- struct sa_proto_plugin *sap_proto_list; static struct sa_proto_handle sa_proto_handle; + extern mutex_t sa_global_lock; + void proto_plugin_fini(); + static void proto_plugin_fini_impl(boolean_t); + /* * Returns true if name is "." or "..", otherwise returns false. */ static boolean_t proto_is_dot_or_dotdot(const char *name)
*** 93,108 **** --- 97,118 ---- struct dirent *dent; int ret = SA_OK; struct stat st; char isa[MAXISALEN]; + assert(MUTEX_HELD(&sa_global_lock)); + #if defined(_LP64) if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1) isa[0] = '\0'; #else isa[0] = '\0'; #endif + if (sap_proto_list != NULL && sa_proto_handle.sa_proto != NULL) { + sa_proto_handle.sa_ref_count++; + return (SA_OK); + } if ((dir = opendir(SA_LIB_DIR)) == NULL) return (SA_OK); while ((dent = readdir(dir)) != NULL) {
*** 153,163 **** sap_proto_list = proto; } (void) closedir(dir); ! if (num_protos != 0) { sa_proto_handle.sa_proto = (char **)calloc(num_protos, sizeof (char *)); sa_proto_handle.sa_ops = (struct sa_plugin_ops **)calloc(num_protos, sizeof (struct sa_plugin_ops *)); --- 163,173 ---- sap_proto_list = proto; } (void) closedir(dir); ! if (num_protos != 0 && sa_proto_handle.sa_proto == NULL) { sa_proto_handle.sa_proto = (char **)calloc(num_protos, sizeof (char *)); sa_proto_handle.sa_ops = (struct sa_plugin_ops **)calloc(num_protos, sizeof (struct sa_plugin_ops *));
*** 192,203 **** } /* * There was an error, so cleanup prior to return of failure. */ ! if (ret != SA_OK) ! proto_plugin_fini(); return (ret); } /* --- 202,217 ---- } /* * There was an error, so cleanup prior to return of failure. */ ! if (ret != SA_OK) { ! proto_plugin_fini_impl(B_TRUE); ! } else { ! assert(sa_proto_handle.sa_ref_count >= 0); ! sa_proto_handle.sa_ref_count++; ! } return (ret); } /*
*** 207,219 **** --- 221,255 ---- */ void proto_plugin_fini() { + assert(MUTEX_HELD(&sa_global_lock)); + + proto_plugin_fini_impl(B_FALSE); + } + + static void + proto_plugin_fini_impl(boolean_t forcefini) + { struct sa_proto_plugin *p; + assert(MUTEX_HELD(&sa_global_lock)); + + sa_proto_handle.sa_ref_count--; /* + * If another thread has a reference to the proto list, + * we don't want to clear it out while they're using it + * or find_protocol() could fail. + */ + if (!forcefini && sa_proto_handle.sa_ref_count > 0) { + return; + } else { + sa_proto_handle.sa_ref_count = 0; + } + + /* * Protocols may call this framework during _fini * (the smbfs plugin is known to do this) so do * two passes: 1st call _fini; 2nd free, dlclose. */ for (p = sap_proto_list; p != NULL; p = p->plugin_next)
*** 224,233 **** --- 260,271 ---- if (p->plugin_handle != NULL) (void) dlclose(p->plugin_handle); free(p); } + if (sap_proto_list != NULL) + sap_proto_list = NULL; if (sa_proto_handle.sa_ops != NULL) { free(sa_proto_handle.sa_ops); sa_proto_handle.sa_ops = NULL; } if (sa_proto_handle.sa_proto != NULL) {
*** 247,257 **** static struct sa_plugin_ops * find_protocol(char *proto) { int i; struct sa_plugin_ops *ops = NULL; - extern mutex_t sa_global_lock; (void) mutex_lock(&sa_global_lock); if (proto != NULL) { for (i = 0; i < sa_proto_handle.sa_num_proto; i++) { if (strcmp(proto, sa_proto_handle.sa_proto[i]) == 0) { --- 285,294 ----