Do not leak mount references for dying threads.

Thread might create a condition for delayed SU cleanup, which creates
a reference to the mount point in td_su, but exit without returning
through userret(), e.g. when terminating due to single-threading or
process exit.  In this case, td_su reference is not dropped and mount
point cannot be freed.

Handle the situation by clearing td_su also in the thread destructor
and in exit1().  softdep_ast_cleanup() has to receive the thread as
argument, since e.g. thread destructor is executed in different
context.

Reported and tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2017-02-25 10:38:18 +00:00
parent d360b49b1d
commit aca4bb9112
7 changed files with 22 additions and 13 deletions

View File

@ -304,8 +304,7 @@ nfssvc_program(struct svc_req *rqst, SVCXPRT *xprt)
svc_freereq(rqst);
out:
if (softdep_ast_cleanup != NULL)
softdep_ast_cleanup();
td_softdep_cleanup(curthread);
NFSEXITCODE(0);
}

View File

@ -207,8 +207,7 @@ exit1(struct thread *td, int rval, int signo)
/*
* Deref SU mp, since the thread does not return to userspace.
*/
if (softdep_ast_cleanup != NULL)
softdep_ast_cleanup();
td_softdep_cleanup(td);
/*
* MUST abort all other threads before proceeding past here.

View File

@ -192,6 +192,8 @@ thread_dtor(void *mem, int size, void *arg)
#endif
/* Free all OSD associated to this thread. */
osd_thread_exit(td);
td_softdep_cleanup(td);
MPASS(td->td_su == NULL);
EVENTHANDLER_INVOKE(thread_dtor, td);
tid_free(td->td_tid);

View File

@ -86,7 +86,7 @@ __FBSDID("$FreeBSD$");
#include <security/mac/mac_framework.h>
void (*softdep_ast_cleanup)(void);
void (*softdep_ast_cleanup)(struct thread *);
/*
* Define the code needed before returning to user mode, for trap and
@ -128,8 +128,8 @@ userret(struct thread *td, struct trapframe *frame)
#ifdef KTRACE
KTRUSERRET(td);
#endif
if (softdep_ast_cleanup != NULL)
softdep_ast_cleanup();
td_softdep_cleanup(td);
MPASS(td->td_su == NULL);
/*
* If this thread tickled GEOM, we need to wait for the giggling to

View File

@ -1114,6 +1114,15 @@ td_get_sched(struct thread *td)
return ((struct td_sched *)&td[1]);
}
extern void (*softdep_ast_cleanup)(struct thread *);
static __inline void
td_softdep_cleanup(struct thread *td)
{
if (td->td_su != NULL && softdep_ast_cleanup != NULL)
softdep_ast_cleanup(td);
}
#endif /* _KERNEL */
#endif /* !_SYS_PROC_H_ */

View File

@ -452,8 +452,6 @@ void free_unr(struct unrhdr *uh, u_int item);
void intr_prof_stack_use(struct thread *td, struct trapframe *frame);
extern void (*softdep_ast_cleanup)(void);
void counted_warning(unsigned *counter, const char *msg);
__NULLABILITY_PRAGMA_POP

View File

@ -902,7 +902,7 @@ static int pagedep_find(struct pagedep_hashhead *, ino_t, ufs_lbn_t,
static void pause_timer(void *);
static int request_cleanup(struct mount *, int);
static void schedule_cleanup(struct mount *);
static void softdep_ast_cleanup_proc(void);
static void softdep_ast_cleanup_proc(struct thread *);
static int process_worklist_item(struct mount *, int, int);
static void process_removes(struct vnode *);
static void process_truncates(struct vnode *);
@ -13445,15 +13445,13 @@ schedule_cleanup(struct mount *mp)
}
static void
softdep_ast_cleanup_proc(void)
softdep_ast_cleanup_proc(struct thread *td)
{
struct thread *td;
struct mount *mp;
struct ufsmount *ump;
int error;
bool req;
td = curthread;
while ((mp = td->td_su) != NULL) {
td->td_su = NULL;
error = vfs_busy(mp, MBF_NOWAIT);
@ -13491,6 +13489,10 @@ softdep_ast_cleanup_proc(void)
}
vfs_unbusy(mp);
}
if ((mp = td->td_su) != NULL) {
td->td_su = NULL;
vfs_rel(mp);
}
}
/*