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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