accept_fd_leak.c revision 168273
1132295Srwatson/*- 2132295Srwatson * Copyright (c) 2004 Robert N. M. Watson 3132295Srwatson * All rights reserved. 4132295Srwatson * 5132295Srwatson * Redistribution and use in source and binary forms, with or without 6132295Srwatson * modification, are permitted provided that the following conditions 7132295Srwatson * are met: 8132295Srwatson * 1. Redistributions of source code must retain the above copyright 9132295Srwatson * notice, this list of conditions and the following disclaimer. 10132295Srwatson * 2. Redistributions in binary form must reproduce the above copyright 11132295Srwatson * notice, this list of conditions and the following disclaimer in the 12132295Srwatson * documentation and/or other materials provided with the distribution. 13132295Srwatson * 14132295Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15132295Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16132295Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17132295Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18132295Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19132295Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20132295Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21132295Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22132295Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23132295Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24132295Srwatson * SUCH DAMAGE. 25132295Srwatson * 26132295Srwatson * $FreeBSD: head/tools/regression/sockets/accept_fd_leak/accept_fd_leak.c 168273 2007-04-02 16:02:50Z jhb $ 27132295Srwatson */ 28132295Srwatson 29132295Srwatson#include <sys/types.h> 30132295Srwatson#include <sys/socket.h> 31168273Sjhb#include <sys/wait.h> 32132295Srwatson 33132295Srwatson#include <netinet/in.h> 34132295Srwatson 35136843Srwatson#include <err.h> 36132295Srwatson#include <errno.h> 37132295Srwatson#include <fcntl.h> 38168273Sjhb#include <signal.h> 39132295Srwatson#include <stdio.h> 40132295Srwatson#include <stdlib.h> 41132295Srwatson#include <string.h> 42132295Srwatson#include <unistd.h> 43132295Srwatson 44132295Srwatson#define LOOPS 500 45132295Srwatson 46168273Sjhbvolatile int quit; 47168273Sjhb 48168273Sjhbstatic void 49168273Sjhbchild_died(int sig) 50168273Sjhb{ 51168273Sjhb quit = 1; 52168273Sjhb} 53168273Sjhb 54132295Srwatson/* 55132295Srwatson * This test is intended to detect a leak of a file descriptor in the process 56132295Srwatson * following a failed non-blocking accept. It measures an available fd 57132295Srwatson * baseline, then performs 1000 failing accepts, then checks to see what the 58132295Srwatson * next fd is. It relies on sequential fd allocation, and will test for it 59132295Srwatson * briefly before beginning (not 100% reliable, but a good start). 60132295Srwatson */ 61132295Srwatsonint 62132295Srwatsonmain(int argc, char *argv[]) 63132295Srwatson{ 64132295Srwatson struct sockaddr_in sin; 65132295Srwatson socklen_t size; 66168273Sjhb pid_t child; 67132295Srwatson int fd1, fd2, fd3, i, s; 68168273Sjhb int status; 69132295Srwatson 70168273Sjhb printf("1..2\n"); 71137587Snik 72132295Srwatson /* 73132295Srwatson * Check for sequential fd allocation, and give up early if not. 74132295Srwatson */ 75132295Srwatson fd1 = dup(STDIN_FILENO); 76132295Srwatson fd2 = dup(STDIN_FILENO); 77136843Srwatson if (fd2 != fd1 + 1) 78136843Srwatson errx(-1, "Non-sequential fd allocation\n"); 79132295Srwatson 80132295Srwatson s = socket(PF_INET, SOCK_STREAM, 0); 81136843Srwatson if (s == -1) 82136843Srwatson errx(-1, "socket: %s", strerror(errno)); 83132295Srwatson 84132295Srwatson bzero(&sin, sizeof(sin)); 85132295Srwatson sin.sin_len = sizeof(sin); 86132295Srwatson sin.sin_family = AF_INET; 87132295Srwatson sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 88132295Srwatson sin.sin_port = htons(8080); 89132295Srwatson 90136843Srwatson if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) != 0) 91136843Srwatson errx(-1, "bind: %s", strerror(errno)); 92132295Srwatson 93136843Srwatson if (listen(s, -1) != 0) 94136843Srwatson errx(-1, "listen: %s", strerror(errno)); 95132295Srwatson 96132295Srwatson i = fcntl(s, F_GETFL); 97136843Srwatson if (i == -1) 98136843Srwatson errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 99132295Srwatson i |= O_NONBLOCK; 100136843Srwatson if (fcntl(s, F_SETFL, i) != 0) 101136843Srwatson errx(-1, "ioctl(F_SETFL): %s", strerror(errno)); 102132295Srwatson i = fcntl(s, F_GETFL); 103136843Srwatson if (i == -1) 104136843Srwatson errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 105136843Srwatson if ((i & O_NONBLOCK) != O_NONBLOCK) 106136843Srwatson errx(-1, "Failed to set O_NONBLOCK (i=0x%x)\n", i); 107132295Srwatson 108132295Srwatson for (i = 0; i < LOOPS; i++) { 109134238Srwatson size = sizeof(sin); 110136843Srwatson if (accept(s, (struct sockaddr *)&sin, &size) != -1) 111136843Srwatson errx(-1, "accept succeeded\n"); 112136843Srwatson if (errno != EAGAIN) 113136843Srwatson errx(-1, "accept: %s", strerror(errno)); 114132295Srwatson } 115132295Srwatson 116132295Srwatson /* 117132295Srwatson * Allocate a file descriptor and make sure it's fd2+2. 2 because 118132295Srwatson * we allocate an fd for the socket. 119132295Srwatson */ 120132295Srwatson fd3 = dup(STDIN_FILENO); 121136843Srwatson if (fd3 != fd2 + 2) 122137587Snik printf("not ok 1 - (%d, %d, %d)\n", fd1, fd2, fd3); 123136843Srwatson else 124137587Snik printf("ok 1\n"); 125132295Srwatson 126168273Sjhb /* 127168273Sjhb * Try failing accept's w/o non-blocking where the destination 128168273Sjhb * address pointer is invalid. 129168273Sjhb */ 130168273Sjhb close(fd3); 131168273Sjhb signal(SIGCHLD, child_died); 132168273Sjhb child = fork(); 133168273Sjhb if (child < 0) 134168273Sjhb errx(-1, "fork: %s", strerror(errno)); 135168273Sjhb 136168273Sjhb /* 137168273Sjhb * Child process does 1000 connect's. 138168273Sjhb */ 139168273Sjhb if (child == 0) { 140168273Sjhb bzero(&sin, sizeof(sin)); 141168273Sjhb sin.sin_len = sizeof(sin); 142168273Sjhb sin.sin_family = AF_INET; 143168273Sjhb sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 144168273Sjhb sin.sin_port = htons(8080); 145168273Sjhb 146168273Sjhb for (i = 0; i < 1000; i++) { 147168273Sjhb s = socket(PF_INET, SOCK_STREAM, 0); 148168273Sjhb if (s == -1) 149168273Sjhb errx(-1, "socket: %s", strerror(errno)); 150168273Sjhb if (connect(s, (struct sockaddr *)&sin, 151168273Sjhb sizeof(sin)) < 0) 152168273Sjhb errx(-1, "connect: %s", strerror(errno)); 153168273Sjhb close(s); 154168273Sjhb } 155168273Sjhb exit(0); 156168273Sjhb } 157168273Sjhb 158168273Sjhb /* Reset back to a blocking socket. */ 159168273Sjhb i = fcntl(s, F_GETFL); 160168273Sjhb if (i == -1) 161168273Sjhb errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 162168273Sjhb i &= ~O_NONBLOCK; 163168273Sjhb if (fcntl(s, F_SETFL, i) != 0) 164168273Sjhb errx(-1, "ioctl(F_SETFL): %s", strerror(errno)); 165168273Sjhb i = fcntl(s, F_GETFL); 166168273Sjhb if (i == -1) 167168273Sjhb errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 168168273Sjhb if (i & O_NONBLOCK) 169168273Sjhb errx(-1, "Failed to clear O_NONBLOCK (i=0x%x)\n", i); 170168273Sjhb 171168273Sjhb /* Do 1000 accept's with an invalid pointer. */ 172168273Sjhb for (i = 0; !quit && i < 1000; i++) { 173168273Sjhb size = sizeof(sin); 174168273Sjhb if (accept(s, (struct sockaddr *)(uintptr_t)(0x100), 175168273Sjhb &size) != -1) 176168273Sjhb errx(-1, "accept succeeded\n"); 177168273Sjhb if (errno != EFAULT) 178168273Sjhb errx(-1, "accept: %s", strerror(errno)); 179168273Sjhb } 180168273Sjhb 181168273Sjhb if (waitpid(child, &status, 0) < 0) 182168273Sjhb errx(-1, "waitpid: %s", strerror(errno)); 183168273Sjhb if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 184168273Sjhb warnx("child process died"); 185168273Sjhb 186168273Sjhb /* 187168273Sjhb * Allocate a file descriptor and make sure it's fd2+2. 2 because 188168273Sjhb * we allocate an fd for the socket. 189168273Sjhb */ 190168273Sjhb fd3 = dup(STDIN_FILENO); 191168273Sjhb if (fd3 != fd2 + 2) 192168273Sjhb printf("not ok 2 - (%d, %d, %d)\n", fd1, fd2, fd3); 193168273Sjhb else 194168273Sjhb printf("ok 2\n"); 195168273Sjhb 196132295Srwatson return (0); 197132295Srwatson} 198