From 93665dfffbf60248d989a125f1465f54c6bdc4d3 Mon Sep 17 00:00:00 2001 From: "David E. O'Brien" Date: Fri, 26 Oct 2012 15:44:29 +0000 Subject: [PATCH] Iterate rather than use recursion. We can blow out the kernel stack if there is a long chain of fork(2)s. --- sys/dev/filemon/filemon_wrapper.c | 15 +-- tools/regression/filemon/Makefile | 24 +++- tools/regression/filemon/timed-forkb.c | 177 +++++++++++++++++++++++++ 3 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 tools/regression/filemon/timed-forkb.c diff --git a/sys/dev/filemon/filemon_wrapper.c b/sys/dev/filemon/filemon_wrapper.c index 278faa917e5e..8ea2cb65a55c 100644 --- a/sys/dev/filemon/filemon_wrapper.c +++ b/sys/dev/filemon/filemon_wrapper.c @@ -82,15 +82,14 @@ filemon_pid_check(struct proc *p) { struct filemon *filemon; - TAILQ_FOREACH(filemon, &filemons_inuse, link) { - if (p->p_pid == filemon->pid) - return (filemon); + while (p->p_pptr) { + TAILQ_FOREACH(filemon, &filemons_inuse, link) { + if (p->p_pid == filemon->pid) + return (filemon); + } + p = p->p_pptr; } - - if (p->p_pptr == NULL) - return (NULL); - - return (filemon_pid_check(p->p_pptr)); + return (NULL); } static void diff --git a/tools/regression/filemon/Makefile b/tools/regression/filemon/Makefile index 06d6f64cbbba..0ff57effe29a 100644 --- a/tools/regression/filemon/Makefile +++ b/tools/regression/filemon/Makefile @@ -1,15 +1,33 @@ # $FreeBSD$ -PROG= filemontest +BINS= \ + filemontest \ + timed-forkb + +bins: filemontest timed-forkb +all: bins NO_MAN= WARNS?= 6 CFLAGS+= -I${.CURDIR}/../../../sys +# Should be "WITHOUT_CTF=" below, but stupid infastrurture fails: +# "/usr/share/mk/bsd.own.mk", line 489: WITH_CTF and WITHOUT_CTF can't both be set. +WITHOUT_CDDL= + +CLEANFILES+= ${BINS} + +tests: bins + kldstat | grep filemon + ${MAKE} test + ./timed-forkb + @echo "filemon(4) tests passed." + # Cannot use .OBJDIR -- 'filemontest' expects 'test_script.sh' in . -test: ${PROG} clean-test -.for BIN in ${PROG} ${PROG}32 +#FILEMONTEST32= filemontest32 +test: filemontest clean-test +.for BIN in filemontest ${FILEMONTEST32} cd ${.CURDIR} ; \ for A in 1 2 3 4 5 6 7 8 9 0; do \ for B in 1 2 3 4 5 6 7 8 9 0; do \ diff --git a/tools/regression/filemon/timed-forkb.c b/tools/regression/filemon/timed-forkb.c new file mode 100644 index 000000000000..a7df9998dc8e --- /dev/null +++ b/tools/regression/filemon/timed-forkb.c @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 2012 David O'Brien + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef SLEEP +#define SLEEP 20 /* seconds */ +#endif + +int verbose; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s\n", getprogname()); + fprintf(stderr, "\t\t-n : length of fork(2) chain\n"); + fprintf(stderr, "\t\t-t : limit run-time seconds\n"); + exit(1); + /* NOTREACHED */ +} + +void term(int); +void +term(int signum) +{ + + if (getpid() == getpgrp() || verbose) { + fprintf(stderr, + "pid %d pgroup %d (ppid %d): Received SIGTERM(%d), exiting...\n", + getpid(), getpgrp(), getppid(), signum); + } + exit(1); +} + +void angel_of_mercy(int); +void +angel_of_mercy(int sig __unused) +{ + + signal(SIGALRM, SIG_IGN); /* ignore this signal */ + printf("Master process: alarmed waking up\n"); + killpg(0, SIGTERM); + return; +} + +int bombing_run(unsigned, int); +int +bombing_run(unsigned chainlen, int stime) +{ + struct rusage ru; + pid_t pid, cpid; + int status; + + if (chainlen) { + switch (pid = fork()) { + case -1: + errx(1, "%s: can't fork", __func__); + + case 0: + /* This is the code the child runs. */ + bombing_run(--chainlen, stime); + break; + + default: + /* This is the code the parent runs. */ + if (getpid() == getpgrp()) { + signal(SIGALRM, angel_of_mercy); + alarm(stime); // time for bombing run... + cpid = wait4(pid, &status, 0, &ru); + alarm(0); + printf( + "Cleanly shutting down - pid %d pgroup %d (ppid %d)\n", + getpid(), getpgrp(), getppid()); + } else { + cpid = wait4(pid, &status, 0, &ru); + } + } + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + time_t start /*,tvec*/; + char *endptr, *ctm; + size_t len; + int nflag, tflag; + int ch, k, maxprocperuid; + + (void)signal(SIGTERM, term); + + nflag = 0; + tflag = SLEEP; + + start = time(NULL); + ctm = ctime(&start); + ctm[24] = '\0'; // see: man 3 ctime + fprintf(stderr, "*** fork() generation started on \"%s\" ***\n", ctm); + + while ((ch = getopt(argc, argv, "n:t:v")) != -1) + switch (ch) { + case 'n': + nflag = strtol(optarg, &endptr, 10); + if (nflag <= 0 || *endptr != '\0') + errx(1, "illegal number, -n argument -- %s", + optarg); + break; + case 't': + tflag = strtol(optarg, &endptr, 10); + if (tflag <= 0 || *endptr != '\0') + errx(1, "illegal number, -t argument -- %s", + optarg); + break; + case 'v': + ++verbose; + break; + default: + usage(); + } + argv += optind; + + if (!nflag) { + len = sizeof(maxprocperuid); + k = sysctlbyname("kern.maxprocperuid", &maxprocperuid, &len, + NULL, 0); + assert(k != ENOMEM); + /* Try to allow a shell to still be started. */ + nflag = maxprocperuid - 10; + } + + // Ensure a unique process group to make killing all children easier. + setpgrp(0,0); + printf(" pid %d pgroup %d (ppid %d), %d fork chain over %d sec\n", + getpid(), getpgrp(), getppid(), nflag - 1, tflag); + + return bombing_run(nflag, tflag); +}