git: 4287aa56197f - main - tcp_usr_shutdown: don't cast inp_ppcb to tcpcb before checking inp_flags

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Tue, 28 Dec 2021 16:53:24 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=4287aa56197fc2e37cad07c23b52f9ed4f1a7fd0

commit 4287aa56197fc2e37cad07c23b52f9ed4f1a7fd0
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2021-12-28 16:50:02 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2021-12-28 16:50:02 +0000

    tcp_usr_shutdown: don't cast inp_ppcb to tcpcb before checking inp_flags
    
    While here move out one more erroneous condition out of the epoch and
    common return.  The only functional change is that if we send control
    on a shut down socket we would get EINVAL instead of ECONNRESET.
    
    Reviewed by:    tuexen
    Reported by:    syzbot+8388cf7f401a7b6bece6@syzkaller.appspotmail.com
    Fixes:          f64dc2ab5be38e5366271ef85ea90d8cb1c7841a
---
 sys/netinet/tcp_usrreq.c | 39 ++++++++++++++++++---------------------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index 2dea7253e0d6..def7d477c72b 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -993,34 +993,31 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf *m,
 	bool restoreflags;
 	TCPDEBUG0;
 
-	/*
-	 * We require the pcbinfo "read lock" if we will close the socket
-	 * as part of this call.
-	 */
-	NET_EPOCH_ENTER(et);
-	inp = sotoinpcb(so);
-	KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL"));
-	INP_WLOCK(inp);
-	tp = intotcpcb(inp);
-	vflagsav = inp->inp_vflag;
-	incflagsav = inp->inp_inc.inc_flags;
-	restoreflags = false;
-	if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
-		if (control)
-			m_freem(control);
-		error = ECONNRESET;
-		goto out;
-	}
 	if (control != NULL) {
 		/* TCP doesn't do control messages (rights, creds, etc) */
 		if (control->m_len) {
 			m_freem(control);
-			error = EINVAL;
-			goto out;
+			return (EINVAL);
 		}
 		m_freem(control);	/* empty control, just free it */
-		control = NULL;
 	}
+
+	inp = sotoinpcb(so);
+	KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL"));
+	INP_WLOCK(inp);
+	if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) {
+		if (m != NULL && (flags & PRUS_NOTREADY) == 0)
+			m_freem(m);
+		INP_WUNLOCK(inp);
+		return (ECONNRESET);
+	}
+
+	vflagsav = inp->inp_vflag;
+	incflagsav = inp->inp_inc.inc_flags;
+	restoreflags = false;
+	tp = intotcpcb(inp);
+
+	NET_EPOCH_ENTER(et);
 	if ((flags & PRUS_OOB) != 0 &&
 	    (error = tcp_pru_options_support(tp, PRUS_OOB)) != 0)
 		goto out;