From b203d25656e29cf10a1cb1af243c368a3582c652 Mon Sep 17 00:00:00 2001 From: Joseph Henry Date: Thu, 22 Oct 2015 12:40:01 -0700 Subject: [PATCH] PhySocket NULL check added to handle_write. Fixed after-close handle_write segfault --- netcon/Intercept.c | 26 +++++++++++------ netcon/NetconEthernetTap.cpp | 53 ++++++++++++++++++----------------- netcon/README.md | 9 +++--- netcon/libintercept.so.1.0 | Bin 47704 -> 47488 bytes 4 files changed, 49 insertions(+), 39 deletions(-) diff --git a/netcon/Intercept.c b/netcon/Intercept.c index 44f648817..a953cf61b 100755 --- a/netcon/Intercept.c +++ b/netcon/Intercept.c @@ -166,7 +166,7 @@ int get_retval() return retval; } } - dwr("unable to read connect: return value\n"); + dwr("unable to read return value\n"); return -1; } @@ -342,7 +342,7 @@ int getsockopt(GETSOCKOPT_SIG) int socket(SOCKET_SIG) { - //dwr("socket()*:\n"); + dwr("socket()*:\n"); int err; #ifdef CHECKS /* Check that type makes sense */ @@ -375,6 +375,11 @@ int socket(SOCKET_SIG) char cmd[BUF_SZ]; fdret_sock = !is_initialized ? init_service_connection() : fdret_sock; + if(fdret_sock < 0) + { + dwr("BAD service connection. exiting.\n"); + exit(-1); + } if(socket_family == AF_LOCAL || socket_family == AF_NETLINK @@ -405,18 +410,21 @@ int socket(SOCKET_SIG) memset(cmd, '\0', BUF_SZ); cmd[0] = RPC_FD_MAP_COMPLETION; memcpy(&cmd[1], &newfd, sizeof(newfd)); - if(newfd > -1) { + + //if(newfd > -1) { send_command(fdret_sock, cmd); pthread_mutex_unlock(&lock); errno = ERR_OK; // OK return newfd; - } + //} + /* else { // Try to read retval+errno since we RXed a bad fd dwr("Error, service sent bad fd.\n"); err = get_retval(); pthread_mutex_unlock(&lock); return err; } + */ } else { dwr("Error while receiving new FD.\n"); @@ -434,7 +442,7 @@ int socket(SOCKET_SIG) connect() intercept function */ int connect(CONNECT_SIG) { - //dwr("connect()*:\n"); + dwr("connect()*:\n"); struct sockaddr_in *connaddr; connaddr = (struct sockaddr_in *) __addr; @@ -520,7 +528,7 @@ int select(SELECT_SIG) bind() intercept function */ int bind(BIND_SIG) { - //dwr("bind()*:\n"); + dwr("bind()*:\n"); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { @@ -582,7 +590,7 @@ int bind(BIND_SIG) /* int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags */ int accept4(ACCEPT4_SIG) { - //dwr("accept4()*:\n"); + dwr("accept4()*:\n"); #ifdef CHECKS if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) { errno = EINVAL; @@ -608,7 +616,7 @@ int accept4(ACCEPT4_SIG) accept() intercept function */ int accept(ACCEPT_SIG) { - //dwr("accept()*:\n"); + dwr("accept()*:\n"); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { @@ -701,7 +709,7 @@ int accept(ACCEPT_SIG) listen() intercept function */ int listen(LISTEN_SIG) { - //dwr("listen()*:\n"); + dwr("listen()*:\n"); #ifdef CHECKS /* Check that this is a valid fd */ if(fcntl(sockfd, F_GETFD) < 0) { diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index c2e31718c..bdf8020a0 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -276,7 +276,7 @@ void NetconEthernetTap::closeConnection(TcpConnection *conn) close(conn->their_fd); if(conn->dataSock) { close(_phy.getDescriptor(conn->dataSock)); - _phy.close(conn->dataSock); + _phy.close(conn->dataSock,false); } for(int i=0; itcp_listen_with_backlog(conn->pcb, listen_rpc->backlog); #else listening_pcb = lwipstack->tcp_listen(conn->pcb); #endif - // FIXME: Correct return values from this method, most is handled in intercept lib if(listening_pcb != NULL) { conn->pcb = listening_pcb; @@ -1146,34 +1145,36 @@ void NetconEthernetTap::handle_write(TcpConnection *conn) return; } - int read_fd = _phy.getDescriptor(conn->dataSock); - if((r = read(read_fd, (&conn->buf)+conn->idx, sndbuf)) > 0) { - conn->idx += r; - /* Writes data pulled from the client's socket buffer to LWIP. This merely sends the - * data to LWIP to be enqueued and eventually sent to the network. */ - if(r > 0) { - int sz; - // NOTE: this assumes that lwipstack->_lock is locked, either - // because we are in a callback or have locked it manually. - int err = lwipstack->_tcp_write(conn->pcb, &conn->buf, r, TCP_WRITE_FLAG_COPY); - //lwipstack->_tcp_output(conn->pcb); - if(err != ERR_OK) { - fprintf(stderr, "handle_write(): error while writing to PCB, (err = %d)\n", err); - return; + if(conn->dataSock) { + int read_fd = _phy.getDescriptor(conn->dataSock); + if((r = read(read_fd, (&conn->buf)+conn->idx, sndbuf)) > 0) { + conn->idx += r; + /* Writes data pulled from the client's socket buffer to LWIP. This merely sends the + * data to LWIP to be enqueued and eventually sent to the network. */ + if(r > 0) { + int sz; + // NOTE: this assumes that lwipstack->_lock is locked, either + // because we are in a callback or have locked it manually. + int err = lwipstack->_tcp_write(conn->pcb, &conn->buf, r, TCP_WRITE_FLAG_COPY); + //lwipstack->_tcp_output(conn->pcb); + if(err != ERR_OK) { + fprintf(stderr, "handle_write(): error while writing to PCB, (err = %d)\n", err); + return; + } + else { + sz = (conn->idx)-r; + if(sz) { + memmove(&conn->buf, (conn->buf+r), sz); + } + conn->idx -= r; + return; + } } else { - sz = (conn->idx)-r; - if(sz) { - memmove(&conn->buf, (conn->buf+r), sz); - } - conn->idx -= r; + fprintf(stderr, "handle_write(): LWIP stack full\n"); return; } } - else { - fprintf(stderr, "handle_write(): LWIP stack full\n"); - return; - } } } } diff --git a/netcon/README.md b/netcon/README.md index 3c239ae18..693d96c4d 100644 --- a/netcon/README.md +++ b/netcon/README.md @@ -1,11 +1,12 @@ Network Containers have been tested with the following: -sshd [ WORKS as of 20151010] Long ~15-20s delay for client during connect -ssh [ WORKS as of 20151010] -curl [ WORKS as of 20151021] +sshd [ WORKS as of 20151022] Long ~15-20s delay for client during connect +ssh [ WORKS as of 20151022] +sftp [ WORKS as of 20151022] +curl [ WORKS as of 20151021] Sometimes "tcp_input: pcb->next != pcb (before cache)" is seen apache (debug mode) [ WORKS as of 2015xxxx] apache (prefork MPM) [ WORKS as of 20151021] -nginx [ WORKS as of 2015xxxx] +nginx [ WORKS as of 20151022] nodejs [ WORKS as of 20151021] java [ WORKS as of 2015xxxx] tomcat [ WORKS as of 2015xxxx] diff --git a/netcon/libintercept.so.1.0 b/netcon/libintercept.so.1.0 index 3037aeb5a940abcf69c31369892deb2a78279d5a..7f604256520020bea6e1f26faa645ab0df4132ea 100755 GIT binary patch delta 12847 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 delta 13048 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