From owner-freebsd-bugs Mon Sep 27 9:20: 9 1999 Delivered-To: freebsd-bugs@freebsd.org Received: from freefall.freebsd.org (freefall.FreeBSD.ORG [204.216.27.21]) by hub.freebsd.org (Postfix) with ESMTP id E69B114DFB for ; Mon, 27 Sep 1999 09:20:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.9.3/8.9.2) id JAA57875; Mon, 27 Sep 1999 09:20:01 -0700 (PDT) (envelope-from gnats@FreeBSD.org) Received: from abb.zenon.net (abb.zenon.net [195.2.64.43]) by hub.freebsd.org (Postfix) with ESMTP id DA3681536F for ; Mon, 27 Sep 1999 09:10:07 -0700 (PDT) (envelope-from abb@abb.zenon.net) Received: (from abb@localhost) by abb.zenon.net (8.9.3/8.9.3) id UAA13312; Mon, 27 Sep 1999 20:12:19 GMT (envelope-from abb) Message-Id: <199909272012.UAA13312@abb.zenon.net> Date: Mon, 27 Sep 1999 20:12:19 GMT From: abb@zenon.net Reply-To: abb@zenon.net To: FreeBSD-gnats-submit@freebsd.org X-Send-Pr-Version: 3.2 Subject: kern/13997: RLIMIT_NPROC works unadequately for jails (patch included) Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.org >Number: 13997 >Category: kern >Synopsis: RLIMIT_NPROC works unadequately for jails (patch included) >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Sep 27 09:20:01 PDT 1999 >Closed-Date: >Last-Modified: >Originator: Alexander Bezroutchko >Release: FreeBSD 4.0-CURRENT i386 >Organization: Zenon NSP >Environment: 4.0-19990918-CURRENT >Description: The fork() syscall checks RLIMIT_NPROC resource limit using system-wide table `uihashtbl'. So limitation on number of running processes will work unadequately if processes run in different jails but have equal uids. >How-To-Repeat: Run two shells with equal uids (not root) in different jails with RLIMIT_NPROC set to 5. Invoke 3 subshells in one jail. Now it is impossible to run any subprocess in shell in another jail because fork fails. >Fix: Create private `uihashtbl' for each jail. diff -c -r sys/jail.h.orig sys/jail.h *** sys/jail.h.orig Mon Sep 27 11:57:59 1999 --- sys/jail.h Mon Sep 27 14:00:18 1999 *************** *** 29,34 **** --- 29,36 ---- MALLOC_DECLARE(M_PRISON); #endif + #include + /* * This structure describes a prison. It is pointed to by all struct * proc's of the inmates. pr_ref keeps track of them and is used to *************** *** 40,45 **** --- 42,49 ---- char pr_host[MAXHOSTNAMELEN]; u_int32_t pr_ip; void *pr_linux; + struct uihashhead *pr_uihashtbl; + u_long pr_uihash; }; #endif /* !KERNEL */ diff -c -r sys/proc.h.orig sys/proc.h *** sys/proc.h.orig Mon Sep 27 11:58:00 1999 --- sys/proc.h Mon Sep 27 12:21:24 1999 *************** *** 107,112 **** --- 107,116 ---- */ struct jail; + struct prison; + + struct uidinfo; + LIST_HEAD(uihashhead, uidinfo); struct proc { TAILQ_ENTRY(proc) p_procq; /* run/sleep queue. */ *************** *** 373,379 **** struct vm_zone; extern struct vm_zone *proc_zone; ! int chgproccnt __P((uid_t uid, int diff)); int enterpgrp __P((struct proc *p, pid_t pgid, int mksess)); void fixjobc __P((struct proc *p, struct pgrp *pgrp, int entering)); int inferior __P((struct proc *p)); --- 377,383 ---- struct vm_zone; extern struct vm_zone *proc_zone; ! int chgproccnt __P((const struct prison *prison, uid_t uid, int diff)); int enterpgrp __P((struct proc *p, pid_t pgid, int mksess)); void fixjobc __P((struct proc *p, struct pgrp *pgrp, int entering)); int inferior __P((struct proc *p)); diff -c -r kern/init_main.c.orig kern/init_main.c *** kern/init_main.c.orig Mon Sep 27 11:57:11 1999 --- kern/init_main.c Mon Sep 27 12:05:39 1999 *************** *** 396,402 **** /* * Charge root for one process. */ ! (void)chgproccnt(0, 1); /* * Initialize the current process pointer (curproc) before --- 396,402 ---- /* * Charge root for one process. */ ! (void)chgproccnt(NULL, 0, 1); /* * Initialize the current process pointer (curproc) before diff -c -r kern/kern_exit.c.orig kern/kern_exit.c *** kern/kern_exit.c.orig Mon Sep 27 11:57:11 1999 --- kern/kern_exit.c Mon Sep 27 12:57:55 1999 *************** *** 488,494 **** /* * Decrement the count of procs running with this uid. */ ! (void)chgproccnt(p->p_cred->p_ruid, -1); /* * Release reference to text vnode --- 488,494 ---- /* * Decrement the count of procs running with this uid. */ ! (void)chgproccnt(p->p_prison, p->p_cred->p_ruid, -1); /* * Release reference to text vnode *************** *** 509,514 **** --- 509,524 ---- * Destroy empty prisons */ if (p->p_prison && !--p->p_prison->pr_ref) { + #ifdef DIAGNOSTIC + u_long i; + struct prison *pr = p->p_prison; + for(i = 0; i <= pr->pr_uihash; i++) { + if (!LIST_EMPTY(&pr->pr_uihashtbl[i])) { + panic("pr_uihashtbl not empty"); + } + } + #endif + FREE(p->p_prison->pr_uihashtbl, M_PRISON); if (p->p_prison->pr_linux != NULL) FREE(p->p_prison->pr_linux, M_PRISON); FREE(p->p_prison, M_PRISON); diff -c -r kern/kern_fork.c.orig kern/kern_fork.c *** kern/kern_fork.c.orig Mon Sep 27 11:57:11 1999 --- kern/kern_fork.c Mon Sep 27 15:06:34 1999 *************** *** 222,230 **** * Increment the count of procs running with this uid. Don't allow * a nonprivileged user to exceed their current limit. */ ! count = chgproccnt(uid, 1); if (uid != 0 && count > p1->p_rlimit[RLIMIT_NPROC].rlim_cur) { ! (void)chgproccnt(uid, -1); /* * Back out the process count */ --- 222,230 ---- * Increment the count of procs running with this uid. Don't allow * a nonprivileged user to exceed their current limit. */ ! count = chgproccnt(p1->p_prison, uid, 1); if (uid != 0 && count > p1->p_rlimit[RLIMIT_NPROC].rlim_cur) { ! (void)chgproccnt(p1->p_prison, uid, -1); /* * Back out the process count */ diff -c -r kern/kern_jail.c.orig kern/kern_jail.c *** kern/kern_jail.c.orig Mon Sep 27 11:57:11 1999 --- kern/kern_jail.c Mon Sep 27 12:52:01 1999 *************** *** 58,63 **** --- 58,69 ---- pr->pr_ref++; p->p_prison = pr; p->p_flag |= P_JAILED; + + /* The process is being jailed. Assume nested jails are not allowed. */ + /* XXX Perhaps the size of pr_uihashtbl should be decreased */ + pr->pr_uihashtbl = hashinit(maxproc / 16, M_PRISON, &pr->pr_uihash); + chgproccnt(NULL, p->p_cred->p_ruid, -1); + chgproccnt(pr, p->p_cred->p_ruid, 1); return (0); bail: diff -c -r kern/kern_proc.c.orig kern/kern_proc.c *** kern/kern_proc.c.orig Mon Sep 27 11:57:12 1999 --- kern/kern_proc.c Mon Sep 27 13:10:02 1999 *************** *** 49,54 **** --- 49,55 ---- #include #include #include + #include static MALLOC_DEFINE(M_PGRP, "pgrp", "process group header"); MALLOC_DEFINE(M_SESSION, "session", "session header"); *************** *** 65,72 **** uid_t ui_uid; long ui_proccnt; }; ! #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) ! static LIST_HEAD(uihashhead, uidinfo) *uihashtbl; static u_long uihash; /* size of hash table - 1 */ static void orphanpg __P((struct pgrp *pg)); --- 66,73 ---- uid_t ui_uid; long ui_proccnt; }; ! #define UIHASH(uihashtbl, uihash, uid) (&uihashtbl[(uid) & uihash]) ! static struct uihashhead *uihashtbl; static u_long uihash; /* size of hash table - 1 */ static void orphanpg __P((struct pgrp *pg)); *************** *** 102,115 **** * a given user is using. */ int ! chgproccnt(uid, diff) uid_t uid; int diff; { register struct uidinfo *uip; register struct uihashhead *uipp; ! uipp = UIHASH(uid); for (uip = uipp->lh_first; uip != 0; uip = uip->ui_hash.le_next) if (uip->ui_uid == uid) break; --- 103,120 ---- * a given user is using. */ int ! chgproccnt(prison, uid, diff) ! const struct prison *prison; uid_t uid; int diff; { register struct uidinfo *uip; register struct uihashhead *uipp; ! if (prison) ! uipp = UIHASH(prison->pr_uihashtbl, prison->pr_uihash, uid); ! else ! uipp = UIHASH(uihashtbl, uihash, uid); for (uip = uipp->lh_first; uip != 0; uip = uip->ui_hash.le_next) if (uip->ui_uid == uid) break; diff -c -r kern/kern_prot.c.orig kern/kern_prot.c *** kern/kern_prot.c.orig Mon Sep 27 11:57:12 1999 --- kern/kern_prot.c Mon Sep 27 12:08:19 1999 *************** *** 414,421 **** * Transfer proc count to new user. */ if (uid != pc->p_ruid) { ! (void)chgproccnt(pc->p_ruid, -1); ! (void)chgproccnt(uid, 1); } /* * Set real uid --- 414,421 ---- * Transfer proc count to new user. */ if (uid != pc->p_ruid) { ! (void)chgproccnt(p->p_prison, pc->p_ruid, -1); ! (void)chgproccnt(p->p_prison, uid, 1); } /* * Set real uid *************** *** 663,670 **** setsugid(p); } if (ruid != (uid_t)-1 && pc->p_ruid != ruid) { ! (void)chgproccnt(pc->p_ruid, -1); ! (void)chgproccnt(ruid, 1); pc->p_ruid = ruid; setsugid(p); } --- 663,670 ---- setsugid(p); } if (ruid != (uid_t)-1 && pc->p_ruid != ruid) { ! (void)chgproccnt(p->p_prison, pc->p_ruid, -1); ! (void)chgproccnt(p->p_prison, ruid, 1); pc->p_ruid = ruid; setsugid(p); } >Release-Note: >Audit-Trail: >Unformatted: To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message