From owner-freebsd-bugs Sat Nov 16 18:10: 8 2002 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 1137937B401 for ; Sat, 16 Nov 2002 18:10:03 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 3A6BC43E75 for ; Sat, 16 Nov 2002 18:10:02 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.6/8.12.6) with ESMTP id gAH2A2x3096010 for ; Sat, 16 Nov 2002 18:10:02 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.6/8.12.6/Submit) id gAH2A2Ul096009; Sat, 16 Nov 2002 18:10:02 -0800 (PST) Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id CFDBA37B401 for ; Sat, 16 Nov 2002 18:03:51 -0800 (PST) Received: from eep.lcs.mit.edu (eep.lcs.mit.edu [18.31.0.114]) by mx1.FreeBSD.org (Postfix) with ESMTP id 37E7543EA3 for ; Sat, 16 Nov 2002 18:03:48 -0800 (PST) (envelope-from dga@eep.lcs.mit.edu) Received: from eep.lcs.mit.edu (localhost [127.0.0.1]) by eep.lcs.mit.edu (8.12.6/8.12.5) with ESMTP id gAH23klC003043 for ; Sat, 16 Nov 2002 21:03:46 -0500 (EST) (envelope-from dga@eep.lcs.mit.edu) Received: (from dga@localhost) by eep.lcs.mit.edu (8.12.6/8.12.3/Submit) id gAH23kdN003042; Sat, 16 Nov 2002 21:03:46 -0500 (EST) Message-Id: <200211170203.gAH23kdN003042@eep.lcs.mit.edu> Date: Sat, 16 Nov 2002 21:03:46 -0500 (EST) From: Dave Andersen Reply-To: Dave Andersen To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Subject: kern/45353: Trivial local DoS via file table exhaustion Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >Number: 45353 >Category: kern >Synopsis: Trivial local DoS via file table exhaustion >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Sat Nov 16 18:10:01 PST 2002 >Closed-Date: >Last-Modified: >Originator: Dave Andersen >Release: FreeBSD 4.7-STABLE i386 >Organization: MIT >Environment: System: FreeBSD eep.lcs.mit.edu 4.7-STABLE FreeBSD 4.7-STABLE #5: Sat Nov 16 17:31:56 EST 2002 root@eep.lcs.mit.edu:/usr/src/sys/compile/EEP46 i386 >Description: As noted in earlier security advisories, it's trivial to exhaust the kernel file table entries and cause a denial of service to other users, including root. While some accidental occurrences of this can be reduced using setrlimit / login.conf limits, it's impractical to prevent a malicious user from stopping the system dead in its tracks, because to do so would require setting per-user proc limit * per-proc FD limit < maxproc Which is impractical. >How-To-Repeat: Run as many separate instances of this as necessary: #include int main() { while (1) { open("/tmp", O_RDONLY); } } >Fix: Attached is a patch that reserves a few file table slots for use by root. This isn't a perfect solution, but the real solution (per-user file limits) is invasive, and may impose excessive accounting overhead. This solution is simple, easy to verify, and provides a channel by which the system administrator can at least get in to the machine to clean up. By default, this patch sets aside the last 5% of the file table for use only by root (i.e. if it's 95% full, then only root may open files). The number is configurable by a sysctl. The patch: diff -r -c sys/kern/kern_descrip.c /usr/src/sys/kern/kern_descrip.c *** sys/kern/kern_descrip.c Mon Apr 29 09:14:12 2002 --- /usr/src/sys/kern/kern_descrip.c Sat Nov 16 14:44:25 2002 *************** *** 849,854 **** --- 849,859 ---- register struct file *fp, *fq; int error, i; + if (nfiles >= (maxfiles - reservefiles) && + p->p_ucred->cr_uid != 0) { + tablefull("non-root file"); + return(ENFILE); + } if (nfiles >= maxfiles) { tablefull("file"); return (ENFILE); *************** *** 1527,1532 **** --- 1532,1540 ---- SYSCTL_INT(_kern, KERN_MAXFILES, maxfiles, CTLFLAG_RW, &maxfiles, 0, "Maximum number of files"); + + SYSCTL_INT(_kern, KERN_RESERVEFILES, reservefiles, CTLFLAG_RW, + &reservefiles, 0, "Number of files reserved for root"); SYSCTL_INT(_kern, OID_AUTO, openfiles, CTLFLAG_RD, &nfiles, 0, "System-wide number of open files"); diff -r -c sys/kern/subr_param.c /usr/src/sys/kern/subr_param.c *** sys/kern/subr_param.c Sat Mar 9 14:05:47 2002 --- /usr/src/sys/kern/subr_param.c Sat Nov 16 14:50:26 2002 *************** *** 62,67 **** --- 62,70 ---- #ifndef MAXFILES #define MAXFILES (maxproc * 2) #endif + #ifndef RESERVEFILES + #define RESERVEFILES (maxfiles / 20) + #endif #ifndef NSFBUFS #define NSFBUFS (512 + maxusers * 16) #endif *************** *** 74,79 **** --- 77,83 ---- int maxprocperuid; /* max # of procs per user */ int maxfiles; /* sys. wide open files limit */ int maxfilesperproc; /* per-proc open files limit */ + int reservefiles; /* Files reserved for root */ int ncallout; /* maximum # of timer events */ int mbuf_wait = 32; /* mbuf sleep time in ticks */ int nbuf; *************** *** 163,168 **** --- 167,174 ---- maxproc = physpages / 12; maxfiles = MAXFILES; TUNABLE_INT_FETCH("kern.maxfiles", &maxfiles); + reservefiles = RESERVEFILES; + TUNABLE_INT_FETCH("kern.reservefiles", &reservefiles); maxprocperuid = (maxproc * 9) / 10; maxfilesperproc = (maxfiles * 9) / 10; diff -r -c sys/sys/file.h /usr/src/sys/sys/file.h *** sys/sys/file.h Sat Jun 2 23:00:10 2001 --- /usr/src/sys/sys/file.h Sat Nov 16 14:58:21 2002 *************** *** 107,112 **** --- 107,113 ---- extern int maxfiles; /* kernel limit on number of open files */ extern int maxfilesperproc; /* per process limit on number of open files */ extern int nfiles; /* actual number of open files */ + extern int reservefiles; /* open files reserved for root */ static __inline void fhold __P((struct file *fp)); int fdrop __P((struct file *fp, struct proc *p)); Only in /usr/src/sys/sys: file.h~ diff -r -c sys/sys/sysctl.h /usr/src/sys/sys/sysctl.h *** sys/sys/sysctl.h Mon Sep 9 13:27:54 2002 --- /usr/src/sys/sys/sysctl.h Sat Nov 16 14:45:53 2002 *************** *** 331,337 **** #define KERN_PS_STRINGS 32 /* int: address of PS_STRINGS */ #define KERN_USRSTACK 33 /* int: address of USRSTACK */ #define KERN_LOGSIGEXIT 34 /* int: do we log sigexit procs? */ ! #define KERN_MAXID 35 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ --- 331,338 ---- #define KERN_PS_STRINGS 32 /* int: address of PS_STRINGS */ #define KERN_USRSTACK 33 /* int: address of USRSTACK */ #define KERN_LOGSIGEXIT 34 /* int: do we log sigexit procs? */ ! #define KERN_RESERVEFILES 35 /* int: number of root-only files */ ! #define KERN_MAXID 36 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ { 0, 0 }, \ *************** *** 369,374 **** --- 370,376 ---- { "ps_strings", CTLTYPE_INT }, \ { "usrstack", CTLTYPE_INT }, \ { "logsigexit", CTLTYPE_INT }, \ + { "reservefiles", CTLTYPE_INT }, \ } /* >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message