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
This commit is contained in:
parent
3764544c53
commit
80e669df9e
@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/eventvar.h>
|
#include <sys/eventvar.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <sys/protosw.h>
|
#include <sys/protosw.h>
|
||||||
|
#include <sys/resourcevar.h>
|
||||||
#include <sys/sigio.h>
|
#include <sys/sigio.h>
|
||||||
#include <sys/signalvar.h>
|
#include <sys/signalvar.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@ -699,9 +700,23 @@ sys_kqueue(struct thread *td, struct kqueue_args *uap)
|
|||||||
struct filedesc *fdp;
|
struct filedesc *fdp;
|
||||||
struct kqueue *kq;
|
struct kqueue *kq;
|
||||||
struct file *fp;
|
struct file *fp;
|
||||||
|
struct proc *p;
|
||||||
|
struct ucred *cred;
|
||||||
int fd, error;
|
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);
|
error = falloc(td, &fp, &fd, 0);
|
||||||
if (error)
|
if (error)
|
||||||
goto done2;
|
goto done2;
|
||||||
@ -711,6 +726,7 @@ sys_kqueue(struct thread *td, struct kqueue_args *uap)
|
|||||||
mtx_init(&kq->kq_lock, "kqueue", NULL, MTX_DEF|MTX_DUPOK);
|
mtx_init(&kq->kq_lock, "kqueue", NULL, MTX_DEF|MTX_DUPOK);
|
||||||
TAILQ_INIT(&kq->kq_head);
|
TAILQ_INIT(&kq->kq_head);
|
||||||
kq->kq_fdp = fdp;
|
kq->kq_fdp = fdp;
|
||||||
|
kq->kq_cred = cred;
|
||||||
knlist_init_mtx(&kq->kq_sel.si_note, &kq->kq_lock);
|
knlist_init_mtx(&kq->kq_sel.si_note, &kq->kq_lock);
|
||||||
TASK_INIT(&kq->kq_task, 0, kqueue_task, kq);
|
TASK_INIT(&kq->kq_task, 0, kqueue_task, kq);
|
||||||
|
|
||||||
@ -723,6 +739,10 @@ sys_kqueue(struct thread *td, struct kqueue_args *uap)
|
|||||||
|
|
||||||
td->td_retval[0] = fd;
|
td->td_retval[0] = fd;
|
||||||
done2:
|
done2:
|
||||||
|
if (error != 0) {
|
||||||
|
chgkqcnt(cred->cr_ruidinfo, -1, 0);
|
||||||
|
crfree(cred);
|
||||||
|
}
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1767,6 +1787,8 @@ kqueue_close(struct file *fp, struct thread *td)
|
|||||||
free(kq->kq_knlist, M_KQUEUE);
|
free(kq->kq_knlist, M_KQUEUE);
|
||||||
|
|
||||||
funsetown(&kq->kq_sigio);
|
funsetown(&kq->kq_sigio);
|
||||||
|
chgkqcnt(kq->kq_cred->cr_ruidinfo, -1, 0);
|
||||||
|
crfree(kq->kq_cred);
|
||||||
free(kq, M_KQUEUE);
|
free(kq, M_KQUEUE);
|
||||||
fp->f_data = NULL;
|
fp->f_data = NULL;
|
||||||
|
|
||||||
|
@ -1432,3 +1432,21 @@ chgptscnt(uip, diff, max)
|
|||||||
}
|
}
|
||||||
return (1);
|
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);
|
||||||
|
}
|
||||||
|
@ -60,6 +60,7 @@ struct kqueue {
|
|||||||
u_long kq_knhashmask; /* size of knhash */
|
u_long kq_knhashmask; /* size of knhash */
|
||||||
struct klist *kq_knhash; /* hash table for knotes */
|
struct klist *kq_knhash; /* hash table for knotes */
|
||||||
struct task kq_task;
|
struct task kq_task;
|
||||||
|
struct ucred *kq_cred;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* !_SYS_EVENTVAR_H_ */
|
#endif /* !_SYS_EVENTVAR_H_ */
|
||||||
|
@ -103,8 +103,9 @@ struct __wrusage {
|
|||||||
#define RLIMIT_AS RLIMIT_VMEM /* standard name for RLIMIT_VMEM */
|
#define RLIMIT_AS RLIMIT_VMEM /* standard name for RLIMIT_VMEM */
|
||||||
#define RLIMIT_NPTS 11 /* pseudo-terminals */
|
#define RLIMIT_NPTS 11 /* pseudo-terminals */
|
||||||
#define RLIMIT_SWAP 12 /* swap used */
|
#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))
|
#define RLIM_INFINITY ((rlim_t)(((uint64_t)1 << 63) - 1))
|
||||||
/* XXX Missing: RLIM_SAVED_MAX, RLIM_SAVED_CUR */
|
/* XXX Missing: RLIM_SAVED_MAX, RLIM_SAVED_CUR */
|
||||||
@ -129,6 +130,7 @@ static const char *rlimit_ident[RLIM_NLIMITS] = {
|
|||||||
"vmem",
|
"vmem",
|
||||||
"npts",
|
"npts",
|
||||||
"swap",
|
"swap",
|
||||||
|
"kqueues",
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -99,6 +99,7 @@ struct uidinfo {
|
|||||||
long ui_sbsize; /* (b) socket buffer space consumed */
|
long ui_sbsize; /* (b) socket buffer space consumed */
|
||||||
long ui_proccnt; /* (b) number of processes */
|
long ui_proccnt; /* (b) number of processes */
|
||||||
long ui_ptscnt; /* (b) number of pseudo-terminals */
|
long ui_ptscnt; /* (b) number of pseudo-terminals */
|
||||||
|
long ui_kqcnt; /* (b) number of kqueues */
|
||||||
uid_t ui_uid; /* (a) uid */
|
uid_t ui_uid; /* (a) uid */
|
||||||
u_int ui_ref; /* (b) reference count */
|
u_int ui_ref; /* (b) reference count */
|
||||||
struct racct *ui_racct; /* (a) resource accounting */
|
struct racct *ui_racct; /* (a) resource accounting */
|
||||||
@ -115,6 +116,7 @@ void addupc_intr(struct thread *td, uintfptr_t pc, u_int ticks);
|
|||||||
void addupc_task(struct thread *td, uintfptr_t pc, u_int ticks);
|
void addupc_task(struct thread *td, uintfptr_t pc, u_int ticks);
|
||||||
void calccru(struct proc *p, struct timeval *up, struct timeval *sp);
|
void calccru(struct proc *p, struct timeval *up, struct timeval *sp);
|
||||||
void calcru(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 chgproccnt(struct uidinfo *uip, int diff, rlim_t maxval);
|
||||||
int chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to,
|
int chgsbsize(struct uidinfo *uip, u_int *hiwat, u_int to,
|
||||||
rlim_t maxval);
|
rlim_t maxval);
|
||||||
|
Loading…
Reference in New Issue
Block a user