Subject: kern/18185: shutdown(s, SHUT_RD); doesn't.
To: None <gnats-bugs@gnats.netbsd.org>
From: None <seanb@qnx.com>
List: netbsd-bugs
Date: 09/05/2002 12:00:39
>Number:         18185
>Category:       kern
>Synopsis:       shutdown(s, SHUT_RD); doesn't.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Sep 05 12:01:01 PDT 2002
>Closed-Date:
>Last-Modified:
>Originator:     Sean Boudreau
>Release:        1-5
>Organization:
QNX
>Environment:
>Description:
After calling shutdown(s, SHUT_RD), data can still be
appended to socket recv buffer.  If data present when
read() and friends called, it will be returned out.
>How-To-Repeat:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <stdio.h>


int
main(int argc, char **argv)
{
        int s, ret;
        struct sockaddr_in sad;
        char buf[] = "This shouldn't have come back.";

        if(argc < 2) {
                fprintf(stderr, "usage: %s host\n", argv[0]);
                return 1;
        }

        memset(&sad, 0x00, sizeof sad);
        sad.sin_family = AF_INET;
        sad.sin_len    = sizeof sad;
        sad.sin_port   = htons(7); /* echo */
        if(inet_aton(argv[1], &sad.sin_addr) == 0) {
                fprintf(stderr, "invalid addr: %s\n", argv[1]);
                return 1;
        }

        if((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
                perror("socket");
                return 1;
        }

        if(connect(s, (struct sockaddr *)&sad, sizeof sad) == -1) {
                perror("connect");
                return 1;
        }

        if(shutdown(s, SHUT_RD) == -1) {
                perror("shutdown");
                return 1;
        }

        if(write(s, buf, sizeof buf) == -1) {
                perror("write");
                return 1;
        }
        /*
         * Attempt to ensure it's already in the recv buffer
         * when read () is called.
         */
        sleep(1);

        if((ret = read(s, buf, sizeof buf - 1)) != 0) {
                if(ret == -1) {
                        perror("read");
                        return 1;
                }
                buf[ret] = '\0';
                fprintf(stderr, "%s\n", buf);
                return 1;
        }

        printf("Success.\n");

        return 0;
}

>Fix:
Appears SS_CANTRCVMORE flag not consulted on fast insertion paths.
Following is against 1-5 branch.  Still appears to be present on head:

Index: netinet/tcp_input.c
===================================================================
RCS file: /cvsroot/syssrc/sys/netinet/tcp_input.c,v
retrieving revision 1.108.4.12
diff -c -r1.108.4.12 tcp_input.c
*** netinet/tcp_input.c 2002/04/03 21:17:06     1.108.4.12
--- netinet/tcp_input.c 2002/09/05 18:50:06
***************
*** 1345,1352 ****
                         * Drop TCP, IP headers and TCP options then add data
                         * to socket buffer.
                         */
!                       m_adj(m, toff + off);
!                       sbappend(&so->so_rcv, m);
                        sorwakeup(so);
                        TCP_SETUP_ACK(tp, th);
                        if (tp->t_flags & TF_ACKNOW)
--- 1345,1357 ----
                         * Drop TCP, IP headers and TCP options then add data
                         * to socket buffer.
                         */
!                       if (so->so_state & SS_CANTRCVMORE) {
!                               m_freem(m);
!                       }
!                       else {
!                               m_adj(m, toff + off);
!                               sbappend(&so->so_rcv, m);
!                       }
                        sorwakeup(so);
                        TCP_SETUP_ACK(tp, th);
                        if (tp->t_flags & TF_ACKNOW)
***************
*** 2084,2091 ****
                        tcpstat.tcps_rcvpack++;
                        tcpstat.tcps_rcvbyte += tlen;
                        ND6_HINT(tp);
!                       m_adj(m, hdroptlen);
!                       sbappend(&(so)->so_rcv, m);
                        sorwakeup(so);
                } else {
                        m_adj(m, hdroptlen);
--- 2089,2101 ----
                        tcpstat.tcps_rcvpack++;
                        tcpstat.tcps_rcvbyte += tlen;
                        ND6_HINT(tp);
!                       if (so->so_state & SS_CANTRCVMORE) {
!                               m_freem(m);
!                       }
!                       else {
!                               m_adj(m, hdroptlen);
!                               sbappend(&(so)->so_rcv, m);
!                       }
                        sorwakeup(so);
                } else {
                        m_adj(m, hdroptlen);


>Release-Note:
>Audit-Trail:
>Unformatted: