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);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to