inpcb: Release the inpcb cred reference before freeing the structure

Now that the inp_cred pointer is accessed only while the inpcb lock is
held, we can avoid deferring a crfree() call when freeing an inpcb.

This fixes a problem introduced when inpcb hash tables started being
synchronized with SMR: the credential reference previously could not be
released until all lockless readers have drained, and there is no
mechanism to explicitly purge cached, freed UMA items.  Thus, ucred
references could linger indefinitely, and since ucreds hold a jail
reference, the jail would linger indefinitely as well.  This manifests
as jails getting stuck in the DYING state.

Discussed with:	glebius
Tested by:	glebius
Sponsored by:	Klara, Inc.
Sponsored by:	Modirum MDPay
Differential Revision:	https://reviews.freebsd.org/D38573
This commit is contained in:
Mark Johnston 2023-04-20 11:48:33 -04:00
parent 7b92493ab1
commit 5fd1a67e88

View File

@ -548,7 +548,6 @@ in_pcbinfo_destroy(struct inpcbinfo *pcbinfo)
/*
* Initialize a pcbstorage - per protocol zones to allocate inpcbs.
*/
static void inpcb_dtor(void *, int, void *);
static void inpcb_fini(void *, int);
void
in_pcbstorage_init(void *arg)
@ -556,7 +555,7 @@ in_pcbstorage_init(void *arg)
struct inpcbstorage *pcbstor = arg;
pcbstor->ips_zone = uma_zcreate(pcbstor->ips_zone_name,
pcbstor->ips_size, NULL, inpcb_dtor, pcbstor->ips_pcbinit,
pcbstor->ips_size, NULL, NULL, pcbstor->ips_pcbinit,
inpcb_fini, UMA_ALIGN_CACHE, UMA_ZONE_SMR);
pcbstor->ips_portzone = uma_zcreate(pcbstor->ips_portzone_name,
sizeof(struct inpcbport), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
@ -1694,6 +1693,10 @@ in_pcbrele_rlocked(struct inpcb *inp)
MPASS(inp->inp_flags & INP_FREED);
MPASS(inp->inp_socket == NULL);
MPASS(inp->inp_in_hpts == 0);
crfree(inp->inp_cred);
#ifdef INVARIANTS
inp->inp_cred = NULL;
#endif
INP_RUNLOCK(inp);
uma_zfree_smr(inp->inp_pcbinfo->ipi_zone, inp);
return (true);
@ -1711,6 +1714,10 @@ in_pcbrele_wlocked(struct inpcb *inp)
MPASS(inp->inp_flags & INP_FREED);
MPASS(inp->inp_socket == NULL);
MPASS(inp->inp_in_hpts == 0);
crfree(inp->inp_cred);
#ifdef INVARIANTS
inp->inp_cred = NULL;
#endif
INP_WUNLOCK(inp);
uma_zfree_smr(inp->inp_pcbinfo->ipi_zone, inp);
return (true);
@ -1788,18 +1795,6 @@ in_pcbfree(struct inpcb *inp)
#endif
#ifdef INET
inp_freemoptions(imo);
#endif
/* Destruction is finalized in inpcb_dtor(). */
}
static void
inpcb_dtor(void *mem, int size, void *arg)
{
struct inpcb *inp = mem;
crfree(inp->inp_cred);
#ifdef INVARIANTS
inp->inp_cred = NULL;
#endif
}