svn commit: r256849 - in head/sys: kern sys

Konstantin Belousov kib at FreeBSD.org
Mon Oct 21 16:44:54 UTC 2013


Author: kib
Date: Mon Oct 21 16:44:53 2013
New Revision: 256849
URL: http://svnweb.freebsd.org/changeset/base/256849

Log:
  Add a resource limit for the total number of kqueues available to the
  user.  Kqueue now saves the ucred of the allocating thread, to
  correctly decrement the counter on close.
  
  Under some specific and not real-world use scenario for kqueue, it is
  possible for the kqueues to consume memory proportional to the square
  of the number of the filedescriptors available to the process.  Limit
  allows administrator to prevent the abuse.
  
  This is kernel-mode side of the change, with the user-mode enabling
  commit following.
  
  Reported and tested by:	pho
  Discussed with:	jmg
  Sponsored by:	The FreeBSD Foundation
  MFC after:	2 weeks

Modified:
  head/sys/kern/kern_event.c
  head/sys/kern/kern_resource.c
  head/sys/sys/eventvar.h
  head/sys/sys/resource.h
  head/sys/sys/resourcevar.h

Modified: head/sys/kern/kern_event.c
==============================================================================
--- head/sys/kern/kern_event.c	Mon Oct 21 16:22:51 2013	(r256848)
+++ head/sys/kern/kern_event.c	Mon Oct 21 16:44:53 2013	(r256849)
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/eventvar.h>
 #include <sys/poll.h>
 #include <sys/protosw.h>
+#include <sys/resourcevar.h>
 #include <sys/sigio.h>
 #include <sys/signalvar.h>
 #include <sys/socket.h>
@@ -699,9 +700,23 @@ sys_kqueue(struct thread *td, struct kqu
 	struct filedesc *fdp;
 	struct kqueue *kq;
 	struct file *fp;
+	struct proc *p;
+	struct ucred *cred;
 	int fd, error;
 
-	fdp = td->td_proc->p_fd;
+	p = td->td_proc;
+	cred = td->td_ucred;
+	crhold(cred);
+	PROC_LOCK(p);
+	if (!chgkqcnt(cred->cr_ruidinfo, 1, lim_cur(td->td_proc,
+	    RLIMIT_KQUEUES))) {
+		PROC_UNLOCK(p);
+		crfree(cred);
+		return (EMFILE);
+	}
+	PROC_UNLOCK(p);
+
+	fdp = p->p_fd;
 	error = falloc(td, &fp, &fd, 0);
 	if (error)
 		goto done2;
@@ -711,6 +726,7 @@ sys_kqueue(struct thread *td, struct kqu
 	mtx_init(&kq->kq_lock, "kqueue", NULL, MTX_DEF|MTX_DUPOK);
 	TAILQ_INIT(&kq->kq_head);
 	kq->kq_fdp = fdp;
+	kq->kq_cred = cred;
 	knlist_init_mtx(&kq->kq_sel.si_note, &kq->kq_lock);
 	TASK_INIT(&kq->kq_task, 0, kqueue_task, kq);
 
@@ -723,6 +739,10 @@ sys_kqueue(struct thread *td, struct kqu
 
 	td->td_retval[0] = fd;
 done2:
+	if (error != 0) {
+		chgkqcnt(cred->cr_ruidinfo, -1, 0);
+		crfree(cred);
+	}
 	return (error);
 }
 
@@ -1767,6 +1787,8 @@ kqueue_close(struct file *fp, struct thr
 		free(kq->kq_knlist, M_KQUEUE);
 
 	funsetown(&kq->kq_sigio);
+	chgkqcnt(kq->kq_cred->cr_ruidinfo, -1, 0);
+	crfree(kq->kq_cred);
 	free(kq, M_KQUEUE);
 	fp->f_data = NULL;
 

Modified: head/sys/kern/kern_resource.c
==============================================================================
--- head/sys/kern/kern_resource.c	Mon Oct 21 16:22:51 2013	(r256848)
+++ head/sys/kern/kern_resource.c	Mon Oct 21 16:44:53 2013	(r256849)
@@ -1432,3 +1432,21 @@ chgptscnt(uip, diff, max)
 	}
 	return (1);
 }
+
+int
+chgkqcnt(struct uidinfo *uip, int diff, rlim_t max)
+{
+
+	if (diff > 0 && max != 0) {
+		if (atomic_fetchadd_long(&uip->ui_kqcnt, (long)diff) +
+		    diff > max) {
+			atomic_subtract_long(&uip->ui_kqcnt, (long)diff);
+			return (0);
+		}
+	} else {
+		atomic_add_long(&uip->ui_kqcnt, (long)diff);
+		if (uip->ui_kqcnt < 0)
+			printf("negative kqcnt for uid = %d\n", uip->ui_uid);
+	}
+	return (1);
+}

Modified: head/sys/sys/eventvar.h
==============================================================================
--- head/sys/sys/eventvar.h	Mon Oct 21 16:22:51 2013	(r256848)
+++ head/sys/sys/eventvar.h	Mon Oct 21 16:44:53 2013	(r256849)
@@ -60,6 +60,7 @@ struct kqueue {
 	u_long		kq_knhashmask;		/* size of knhash */
 	struct		klist *kq_knhash;	/* hash table for knotes */
 	struct		task kq_task;
+	struct		ucred *kq_cred;
 };
 
 #endif /* !_SYS_EVENTVAR_H_ */

Modified: head/sys/sys/resource.h
==============================================================================
--- head/sys/sys/resource.h	Mon Oct 21 16:22:51 2013	(r256848)
+++ head/sys/sys/resource.h	Mon Oct 21 16:44:53 2013	(r256849)
@@ -103,8 +103,9 @@ struct __wrusage {
 #define	RLIMIT_AS	RLIMIT_VMEM	/* standard name for RLIMIT_VMEM */
 #define	RLIMIT_NPTS	11		/* pseudo-terminals */
 #define	RLIMIT_SWAP	12		/* swap used */
+#define	RLIMIT_KQUEUES	13		/* kqueues allocated */
 
-#define	RLIM_NLIMITS	13		/* number of resource limits */
+#define	RLIM_NLIMITS	14		/* number of resource limits */
 
 #define	RLIM_INFINITY	((rlim_t)(((uint64_t)1 << 63) - 1))
 /* XXX Missing: RLIM_SAVED_MAX, RLIM_SAVED_CUR */
@@ -129,6 +130,7 @@ static const char *rlimit_ident[RLIM_NLI
 	"vmem",
 	"npts",
 	"swap",
+	"kqueues",
 };
 #endif
 

Modified: head/sys/sys/resourcevar.h
==============================================================================
--- head/sys/sys/resourcevar.h	Mon Oct 21 16:22:51 2013	(r256848)
+++ head/sys/sys/resourcevar.h	Mon Oct 21 16:44:53 2013	(r256849)
@@ -99,6 +99,7 @@ struct uidinfo {
 	long	ui_sbsize;		/* (b) socket buffer space consumed */
 	long	ui_proccnt;		/* (b) number of processes */
 	long	ui_ptscnt;		/* (b) number of pseudo-terminals */
+	long	ui_kqcnt;		/* (b) number of kqueues */
 	uid_t	ui_uid;			/* (a) uid */
 	u_int	ui_ref;			/* (b) reference count */
 	struct racct *ui_racct;		/* (a) resource accounting */
@@ -115,6 +116,7 @@ void	 addupc_intr(struct thread *td, uin
 void	 addupc_task(struct thread *td, uintfptr_t pc, u_int ticks);
 void	 calccru(struct proc *p, struct timeval *up, struct timeval *sp);
 void	 calcru(struct proc *p, struct timeval *up, struct timeval *sp);
+int	 chgkqcnt(struct uidinfo *uip, int diff, rlim_t max);
 int	 chgproccnt(struct uidinfo *uip, int diff, rlim_t maxval);
 int	 chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to,
 	    rlim_t maxval);


More information about the svn-src-head mailing list