accept_fd_leak.c revision 281974
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: stable/10/tools/regression/sockets/accept_fd_leak/accept_fd_leak.c 281974 2015-04-25 05:31:52Z ngie $ 27132295Srwatson */ 28132295Srwatson 29281974Sngie#include <sys/param.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 44281974Sngie#define BIND_ATTEMPTS 10 45132295Srwatson#define LOOPS 500 46281974Sngie#define NUM_ATTEMPTS 1000 47132295Srwatson 48281974Sngiestatic volatile int quit; 49168273Sjhb 50168273Sjhbstatic void 51281974Sngiechild_died(int sig __unused) 52168273Sjhb{ 53281974Sngie 54168273Sjhb quit = 1; 55168273Sjhb} 56168273Sjhb 57132295Srwatson/* 58132295Srwatson * This test is intended to detect a leak of a file descriptor in the process 59132295Srwatson * following a failed non-blocking accept. It measures an available fd 60132295Srwatson * baseline, then performs 1000 failing accepts, then checks to see what the 61132295Srwatson * next fd is. It relies on sequential fd allocation, and will test for it 62132295Srwatson * briefly before beginning (not 100% reliable, but a good start). 63132295Srwatson */ 64132295Srwatsonint 65281974Sngiemain(void) 66132295Srwatson{ 67132295Srwatson struct sockaddr_in sin; 68132295Srwatson socklen_t size; 69168273Sjhb pid_t child; 70281974Sngie int fd1, fd2, fd3, i, listen_port, s, status; 71132295Srwatson 72168273Sjhb printf("1..2\n"); 73137587Snik 74132295Srwatson /* 75132295Srwatson * Check for sequential fd allocation, and give up early if not. 76132295Srwatson */ 77132295Srwatson fd1 = dup(STDIN_FILENO); 78132295Srwatson fd2 = dup(STDIN_FILENO); 79136843Srwatson if (fd2 != fd1 + 1) 80136843Srwatson errx(-1, "Non-sequential fd allocation\n"); 81132295Srwatson 82132295Srwatson s = socket(PF_INET, SOCK_STREAM, 0); 83136843Srwatson if (s == -1) 84136843Srwatson errx(-1, "socket: %s", strerror(errno)); 85132295Srwatson 86132295Srwatson bzero(&sin, sizeof(sin)); 87132295Srwatson sin.sin_len = sizeof(sin); 88132295Srwatson sin.sin_family = AF_INET; 89132295Srwatson sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 90132295Srwatson 91281974Sngie srandomdev(); 92132295Srwatson 93281974Sngie for (i = 0; i < BIND_ATTEMPTS; i++) { 94281974Sngie /* Pick a random unprivileged port 1025-65535 */ 95281974Sngie listen_port = MAX((int)random() % 65535, 1025); 96281974Sngie sin.sin_port = htons(listen_port); 97281974Sngie if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0) 98281974Sngie break; 99281974Sngie warn("bind with %d failed", listen_port); 100281974Sngie usleep(1000); 101281974Sngie } 102281974Sngie if (i >= BIND_ATTEMPTS) { 103281974Sngie printf("Bail out!\n"); 104281974Sngie exit(1); 105281974Sngie } 106281974Sngie 107136843Srwatson if (listen(s, -1) != 0) 108136843Srwatson errx(-1, "listen: %s", strerror(errno)); 109132295Srwatson 110132295Srwatson i = fcntl(s, F_GETFL); 111136843Srwatson if (i == -1) 112136843Srwatson errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 113132295Srwatson i |= O_NONBLOCK; 114136843Srwatson if (fcntl(s, F_SETFL, i) != 0) 115136843Srwatson errx(-1, "ioctl(F_SETFL): %s", strerror(errno)); 116132295Srwatson i = fcntl(s, F_GETFL); 117136843Srwatson if (i == -1) 118136843Srwatson errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 119136843Srwatson if ((i & O_NONBLOCK) != O_NONBLOCK) 120136843Srwatson errx(-1, "Failed to set O_NONBLOCK (i=0x%x)\n", i); 121132295Srwatson 122132295Srwatson for (i = 0; i < LOOPS; i++) { 123134238Srwatson size = sizeof(sin); 124136843Srwatson if (accept(s, (struct sockaddr *)&sin, &size) != -1) 125136843Srwatson errx(-1, "accept succeeded\n"); 126136843Srwatson if (errno != EAGAIN) 127136843Srwatson errx(-1, "accept: %s", strerror(errno)); 128132295Srwatson } 129132295Srwatson 130132295Srwatson /* 131132295Srwatson * Allocate a file descriptor and make sure it's fd2+2. 2 because 132132295Srwatson * we allocate an fd for the socket. 133132295Srwatson */ 134132295Srwatson fd3 = dup(STDIN_FILENO); 135136843Srwatson if (fd3 != fd2 + 2) 136137587Snik printf("not ok 1 - (%d, %d, %d)\n", fd1, fd2, fd3); 137136843Srwatson else 138137587Snik printf("ok 1\n"); 139132295Srwatson 140168273Sjhb /* 141168273Sjhb * Try failing accept's w/o non-blocking where the destination 142168273Sjhb * address pointer is invalid. 143168273Sjhb */ 144168273Sjhb close(fd3); 145168273Sjhb signal(SIGCHLD, child_died); 146168273Sjhb child = fork(); 147168273Sjhb if (child < 0) 148168273Sjhb errx(-1, "fork: %s", strerror(errno)); 149168273Sjhb 150168273Sjhb /* 151281974Sngie * Child process does `NUM_ATTEMPTS` connects. 152168273Sjhb */ 153168273Sjhb if (child == 0) { 154281974Sngie close(fd1); 155281974Sngie close(fd2); 156281974Sngie close(s); 157281974Sngie 158168273Sjhb bzero(&sin, sizeof(sin)); 159168273Sjhb sin.sin_len = sizeof(sin); 160168273Sjhb sin.sin_family = AF_INET; 161168273Sjhb sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 162281974Sngie sin.sin_port = htons(listen_port); 163168273Sjhb 164281974Sngie for (i = 0; i < NUM_ATTEMPTS; i++) { 165168273Sjhb s = socket(PF_INET, SOCK_STREAM, 0); 166168273Sjhb if (s == -1) 167168273Sjhb errx(-1, "socket: %s", strerror(errno)); 168168273Sjhb if (connect(s, (struct sockaddr *)&sin, 169168273Sjhb sizeof(sin)) < 0) 170168273Sjhb errx(-1, "connect: %s", strerror(errno)); 171168273Sjhb close(s); 172168273Sjhb } 173281974Sngie _exit(0); 174168273Sjhb } 175168273Sjhb 176168273Sjhb /* Reset back to a blocking socket. */ 177168273Sjhb i = fcntl(s, F_GETFL); 178168273Sjhb if (i == -1) 179168273Sjhb errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 180168273Sjhb i &= ~O_NONBLOCK; 181168273Sjhb if (fcntl(s, F_SETFL, i) != 0) 182168273Sjhb errx(-1, "ioctl(F_SETFL): %s", strerror(errno)); 183168273Sjhb i = fcntl(s, F_GETFL); 184168273Sjhb if (i == -1) 185168273Sjhb errx(-1, "ioctl(F_GETFL): %s", strerror(errno)); 186168273Sjhb if (i & O_NONBLOCK) 187168273Sjhb errx(-1, "Failed to clear O_NONBLOCK (i=0x%x)\n", i); 188281974Sngie 189281974Sngie /* Do `NUM_ATTEMPTS` accepts with an invalid pointer. */ 190281974Sngie for (i = 0; !quit && i < NUM_ATTEMPTS; i++) { 191168273Sjhb size = sizeof(sin); 192168273Sjhb if (accept(s, (struct sockaddr *)(uintptr_t)(0x100), 193168273Sjhb &size) != -1) 194168273Sjhb errx(-1, "accept succeeded\n"); 195168273Sjhb if (errno != EFAULT) 196168273Sjhb errx(-1, "accept: %s", strerror(errno)); 197168273Sjhb } 198168273Sjhb 199168273Sjhb if (waitpid(child, &status, 0) < 0) 200168273Sjhb errx(-1, "waitpid: %s", strerror(errno)); 201168273Sjhb if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) 202168273Sjhb warnx("child process died"); 203281974Sngie 204168273Sjhb /* 205168273Sjhb * Allocate a file descriptor and make sure it's fd2+2. 2 because 206168273Sjhb * we allocate an fd for the socket. 207168273Sjhb */ 208168273Sjhb fd3 = dup(STDIN_FILENO); 209168273Sjhb if (fd3 != fd2 + 2) 210168273Sjhb printf("not ok 2 - (%d, %d, %d)\n", fd1, fd2, fd3); 211168273Sjhb else 212168273Sjhb printf("ok 2\n"); 213168273Sjhb 214132295Srwatson return (0); 215132295Srwatson} 216