On 2020-06-26 17:02, J. Bruce Fields wrote:
Unless I'm missing something--an upcall thread could still be using this file descriptor. If we're particularly unlucky, we could do a new open in a moment and reuse this file descriptor number, and then then writes in do_downcall() could end up going to some other random file. I think we want these closes done by gssd_free_client() in the !refcnt case?
Makes sense. I was thinking more that it was an abort situation and we shouldn't be sending any data to the kernel but re-use is definitely a concern.
I've split it so that we are removed from the event loop in destroy() but the close happens in free().
Doug
From 8ef49081e8a42bfa05bb63265cd4f951e2b23413 Mon Sep 17 00:00:00 2001 From: Doug Nazar <nazard@xxxxxxxx> Date: Fri, 26 Jun 2020 16:02:04 -0400 Subject: [PATCH] gssd: Refcount struct clnt_info to protect multithread usage Struct clnt_info is shared with the various upcall threads so we need to ensure that it stays around even if the client dir gets removed. Reported-by: Sebastian Kraus <sebastian.kraus@xxxxxxxxxxxx> Signed-off-by: Doug Nazar <nazard@xxxxxxxx> --- utils/gssd/gssd.c | 67 ++++++++++++++++++++++++++++++++---------- utils/gssd/gssd.h | 5 ++-- utils/gssd/gssd_proc.c | 4 +-- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c index 588da0fb..b40c3220 100644 --- a/utils/gssd/gssd.c +++ b/utils/gssd/gssd.c @@ -90,9 +90,7 @@ char *ccachedir = NULL; /* Avoid DNS reverse lookups on server names */ static bool avoid_dns = true; static bool use_gssproxy = false; -int thread_started = false; -pthread_mutex_t pmutex = PTHREAD_MUTEX_INITIALIZER; -pthread_cond_t pcond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t clp_lock = PTHREAD_MUTEX_INITIALIZER; TAILQ_HEAD(topdir_list_head, topdir) topdir_list; @@ -359,20 +357,28 @@ out: free(port); } +/* Actually frees clp and fields that might be used from other + * threads if was last reference. + */ static void -gssd_destroy_client(struct clnt_info *clp) +gssd_free_client(struct clnt_info *clp) { - if (clp->krb5_fd >= 0) { + int refcnt; + + pthread_mutex_lock(&clp_lock); + refcnt = --clp->refcount; + pthread_mutex_unlock(&clp_lock); + if (refcnt > 0) + return; + + printerr(3, "freeing client %s\n", clp->relpath); + + if (clp->krb5_fd >= 0) close(clp->krb5_fd); - event_del(&clp->krb5_ev); - } - if (clp->gssd_fd >= 0) { + if (clp->gssd_fd >= 0) close(clp->gssd_fd); - event_del(&clp->gssd_ev); - } - inotify_rm_watch(inotify_fd, clp->wd); free(clp->relpath); free(clp->servicename); free(clp->servername); @@ -380,6 +386,24 @@ gssd_destroy_client(struct clnt_info *clp) free(clp); } +/* Called when removing from clnt_list to tear down event handling. + * Will then free clp if was last reference. + */ +static void +gssd_destroy_client(struct clnt_info *clp) +{ + printerr(3, "destroying client %s\n", clp->relpath); + + if (clp->krb5_fd >= 0) + event_del(&clp->krb5_ev); + + if (clp->gssd_fd >= 0) + event_del(&clp->gssd_ev); + + inotify_rm_watch(inotify_fd, clp->wd); + gssd_free_client(clp); +} + static void gssd_scan(void); static int @@ -416,11 +440,21 @@ static struct clnt_upcall_info *alloc_upcall_info(struct clnt_info *clp) info = malloc(sizeof(struct clnt_upcall_info)); if (info == NULL) return NULL; + + pthread_mutex_lock(&clp_lock); + clp->refcount++; + pthread_mutex_unlock(&clp_lock); info->clp = clp; return info; } +void free_upcall_info(struct clnt_upcall_info *info) +{ + gssd_free_client(info->clp); + free(info); +} + /* For each upcall read the upcall info into the buffer, then create a * thread in a detached state so that resources are released back into * the system without the need for a join. @@ -438,13 +472,13 @@ gssd_clnt_gssd_cb(int UNUSED(fd), short UNUSED(which), void *data) info->lbuflen = read(clp->gssd_fd, info->lbuf, sizeof(info->lbuf)); if (info->lbuflen <= 0 || info->lbuf[info->lbuflen-1] != '\n') { printerr(0, "WARNING: %s: failed reading request\n", __func__); - free(info); + free_upcall_info(info); return; } info->lbuf[info->lbuflen-1] = 0; if (start_upcall_thread(handle_gssd_upcall, info)) - free(info); + free_upcall_info(info); } static void @@ -461,12 +495,12 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data) sizeof(info->uid)) < (ssize_t)sizeof(info->uid)) { printerr(0, "WARNING: %s: failed reading uid from krb5 " "upcall pipe: %s\n", __func__, strerror(errno)); - free(info); + free_upcall_info(info); return; } if (start_upcall_thread(handle_krb5_upcall, info)) - free(info); + free_upcall_info(info); } static struct clnt_info * @@ -501,6 +535,7 @@ gssd_get_clnt(struct topdir *tdi, const char *name) clp->name = clp->relpath + strlen(tdi->name) + 1; clp->krb5_fd = -1; clp->gssd_fd = -1; + clp->refcount = 1; TAILQ_INSERT_HEAD(&tdi->clnt_list, clp, list); return clp; @@ -651,7 +686,7 @@ gssd_scan_topdir(const char *name) if (clp->scanned) continue; - printerr(3, "destroying client %s\n", clp->relpath); + printerr(3, "orphaned client %s\n", clp->relpath); saveprev = clp->list.tqe_prev; TAILQ_REMOVE(&tdi->clnt_list, clp, list); gssd_destroy_client(clp); diff --git a/utils/gssd/gssd.h b/utils/gssd/gssd.h index f4f59754..d33e4dff 100644 --- a/utils/gssd/gssd.h +++ b/utils/gssd/gssd.h @@ -63,12 +63,10 @@ extern unsigned int context_timeout; extern unsigned int rpc_timeout; extern char *preferred_realm; extern pthread_mutex_t ple_lock; -extern pthread_cond_t pcond; -extern pthread_mutex_t pmutex; -extern int thread_started; struct clnt_info { TAILQ_ENTRY(clnt_info) list; + int refcount; int wd; bool scanned; char *name; @@ -94,6 +92,7 @@ struct clnt_upcall_info { void handle_krb5_upcall(struct clnt_upcall_info *clp); void handle_gssd_upcall(struct clnt_upcall_info *clp); +void free_upcall_info(struct clnt_upcall_info *info); #endif /* _RPC_GSSD_H_ */ diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c index 8fe6605b..05c1da64 100644 --- a/utils/gssd/gssd_proc.c +++ b/utils/gssd/gssd_proc.c @@ -730,7 +730,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info) printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath); process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL); - free(info); + free_upcall_info(info); } void @@ -830,6 +830,6 @@ handle_gssd_upcall(struct clnt_upcall_info *info) out: free(upcall_str); out_nomem: - free(info); + free_upcall_info(info); return; } -- 2.26.2