From dga@eep.lcs.mit.edu  Sat Nov 16 18:03:51 2002
Return-Path: <dga@eep.lcs.mit.edu>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id CFDBA37B401
	for <FreeBSD-gnats-submit@freebsd.org>; 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 <FreeBSD-gnats-submit@freebsd.org>; 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 <FreeBSD-gnats-submit@freebsd.org>; 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 <dga@lcs.mit.edu>
Reply-To: Dave Andersen <dga@lcs.mit.edu>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: Trivial local DoS via file table exhaustion
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         45353
>Category:       kern
>Synopsis:       Trivial local DoS via file table exhaustion
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    silby
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sat Nov 16 18:10:01 PST 2002
>Closed-Date:    Wed Jun 25 20:55:45 PDT 2003
>Last-Modified:  Wed Jun 25 20:55:45 PDT 2003
>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 <fcntl.h>

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:
Responsible-Changed-From-To: freebsd-bugs->silby 
Responsible-Changed-By: silby 
Responsible-Changed-When: Thu Nov 21 13:17:50 PST 2002 
Responsible-Changed-Why:  
I'll look into committing this after 5.0-release is out the door. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45353 
State-Changed-From-To: open->closed 
State-Changed-By: silby 
State-Changed-When: Wed Jun 25 20:55:24 PDT 2003 
State-Changed-Why:  
All done, modified patch committed to both -current and -stable. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=45353 
>Unformatted:
