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