Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 16 Nov 2002 21:03:46 -0500 (EST)
From:      Dave Andersen <dga@lcs.mit.edu>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/45353: Trivial local DoS via file table exhaustion
Message-ID:  <200211170203.gAH23kdN003042@eep.lcs.mit.edu>

index | next in thread | raw e-mail


>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 <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:
>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?200211170203.gAH23kdN003042>