Subject: another socket bug
From: [email-masked] (Tim Newsham)

28 Apr 1998 04:59:07 -0700
Message-ID: <[email-masked]>
Mime-Version: 1.0
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
To: [email-masked]

Hi,

    It looks like another cygwin bug.  The following is a test program which
behaves incorrectly in cygwin.  I ran it in cygwin beta 19.1 as:

    remote% nc -l -p 3333
                                    cygwin$ ./testcase

The program connects from cyginw to remote, then it sequentially tries
to connect to a few ports (notice how slow the connection attempts are;
winsock seems to be very slow at reacting to RST's after SYN's).  After
some time a signal handler is invoked which warps the program back
out to main.  At this point a write to the original connected socket
will fail with EINPROGRESS (sometimes.  it appears this is a race).

This should not happen!  My best guess is that the signal handler warped 
out of the cygwin connect() function and cygwin was left in a weird state 
in which future operations fail.  EINPROGRESS is not even a valid
error response for write().

The error can also be observed when sigsetjmp/siglongjmp is not used
but socket operations are performed directly from the signal handler.

fix: not sure.  perhaps some parts of the code need to be protected
from interruption by signals.

                                           Tim N.

---- test case ----
/*
 * Connect to REMOTE port 3333 (I use nc -l -p 3333 on remote host for testing)
 * then attempt to connect to ports on REMOTE until timeout
 * after TIMEOUT, try to write to REMOTE.
 *
 * Error: write() to REMOTE at end (after jumping from signal handler)
 * results in error with errno = 119 sometimes (EINPROGRESS).  This should
 * not happen.  The same thing is observed if the write is done directly
 * from the signal handler.
 *
 * Synopsis: Jumping out of some calls leaves the system in a weird state?
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>

#define REMOTE "10.200.200.3"

static sigjmp_buf jmpbuf;

static void
jumper(int sig)
{
    siglongjmp(jmpbuf, 1);
}

int
connect_sock(int addr, int port)
{
    struct sockaddr_in ad;
    int s;

    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s == -1) {
        perror("socket");
        exit(1);
    }
    memset(&ad, 0, sizeof ad);
    ad.sin_family = AF_INET;
    ad.sin_addr.s_addr = addr;
    ad.sin_port = htons(port);
    if(connect(s, (struct sockaddr *)&ad, sizeof ad) == -1) {
        close(s);
        return -1;
    }
    return s;
}

int
set_nonblock(int fd, int flag)
{
    return ioctl(fd, FIONBIO, &flag);
}

int
main()
{
    int s, sx, port, addr, res;
    char c;

    signal(SIGALRM, jumper);

    sx = connect_sock(inet_addr("10.200.200.3"), 3333);
    printf("sx is %d\n", sx);

    addr = inet_addr("10.200.200.3");
    alarm(5);
    if(sigsetjmp(jmpbuf, 1) == 0) {
        for(port = 1; port < 40; port++) {
            printf("try %d\n", port);
            s = connect_sock(addr, port);
            if(s == -1)
                continue;
            printf("%d connected\n", port);
            set_nonblock(s, 1);
            write(s, "test", 4);
            read(s, &c, 1);
            close(s);
        }
    } else {
        printf("timeout\n");
    }

    res = write(sx, "test", 4);
    if(res == -1)
        printf("error: %s\n", strerror(errno));
    else
        printf("res %d\n", res);
    return 0;
}

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".