diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -681,6 +681,7 @@ bool in_pcbrele(struct inpcb *, inp_lookup_t); bool in_pcbrele_rlocked(struct inpcb *); bool in_pcbrele_wlocked(struct inpcb *); +bool in_pcbrele_rlock(struct inpcb *inp); typedef bool inp_match_t(const struct inpcb *, void *); struct inpcb_iterator { diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1744,6 +1744,23 @@ in_pcbrele_rlocked(inp) : in_pcbrele_wlocked(inp)); } +/* + * Dereference and rlock inp. Returns true if inp no longer usable, + * false otherwise. + */ +bool +in_pcbrele_rlock(struct inpcb *inp) +{ + INP_RLOCK(inp); + if (in_pcbrele_rlocked(inp)) + return (true); + if ((inp->inp_flags & INP_FREED) != 0) { + INP_RUNLOCK(inp); + return (true); + } + return (false); +} + /* * Unconditionally schedule an inpcb to be freed by decrementing its * reference count, which should occur only after the inpcb has been detached diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -2686,6 +2686,7 @@ if (req->newptr != NULL) return (EPERM); +again: len = 0; cnt = 0; ipi_gencnt = V_tcbinfo.ipi_gencnt; @@ -2720,8 +2721,12 @@ ksr->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; + in_pcbref(inp); + INP_RUNLOCK(inp); ksr->snd_tag->sw->snd_tag_status_str( ksr->snd_tag, NULL, &sz); + if (in_pcbrele_rlock(inp)) + goto again; len += sz; } } @@ -2739,8 +2744,11 @@ kss->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; + INP_RUNLOCK(inp); kss->snd_tag->sw->snd_tag_status_str( kss->snd_tag, NULL, &sz); + if (in_pcbrele_rlock(inp)) + goto again; len += sz; } } @@ -2811,8 +2819,11 @@ if (ksr->snd_tag != NULL && ksr->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; + INP_RUNLOCK(inp); ksr->snd_tag->sw->snd_tag_status_str( ksr->snd_tag, buf + len, &sz); + if (in_pcbrele_rlock(inp)) + goto again_reset; len += sz; } } @@ -2828,8 +2839,11 @@ if (kss->snd_tag != NULL && kss->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; + INP_RUNLOCK(inp); kss->snd_tag->sw->snd_tag_status_str( kss->snd_tag, buf + len, &sz); + if (in_pcbrele_rlock(inp)) + goto again_reset; len += sz; } } @@ -2853,6 +2867,10 @@ zfree(buf, M_TEMP); return (error); + +again_reset: + req->oldidx = 0; + goto again; } static int