From abb@abb.zenon.net Mon Sep 27 09:10:15 1999
Return-Path: <abb@abb.zenon.net>
Received: from abb.zenon.net (abb.zenon.net [195.2.64.43])
	by hub.freebsd.org (Postfix) with ESMTP id DA3681536F
	for <FreeBSD-gnats-submit@freebsd.org>; 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
Sender: abb@abb.zenon.net
Reply-To: abb@zenon.net
To: FreeBSD-gnats-submit@freebsd.org
Subject: RLIMIT_NPROC works unadequately for jails (patch included)
X-Send-Pr-Version: 3.2

>Number:         13997
>Category:       kern
>Synopsis:       [jail] [patch] RLIMIT_NPROC works unadequately for jails
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    rwatson
>State:          suspended
>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:  Mon Oct 24 01:55:17 GMT 2005
>Originator:     Alexander Bezroutchko <abb@zenon.net>
>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 <sys/proc.h>
+ 
  /*
   * 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 <vm/vm_map.h>
  #include <sys/user.h>
  #include <vm/vm_zone.h>
+ #include <sys/jail.h>
  
  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:
Responsible-Changed-From-To: freebsd-bugs->phk 
Responsible-Changed-By: sheldonh 
Responsible-Changed-When: Mon Sep 27 09:25:27 PDT 1999 
Responsible-Changed-Why:  
Over to Mr Jail. 
Responsible-Changed-From-To: phk->rwatson 
Responsible-Changed-By: phk 
Responsible-Changed-When: Fri Nov 16 15:02:12 PST 2001 
Responsible-Changed-Why:  
I think Rwatson is better suited to judge this one. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=13997 
State-Changed-From-To: open->suspended 
State-Changed-By: linimon 
State-Changed-When: Mon Oct 24 01:53:55 GMT 2005 
State-Changed-Why:  
Mark as 'suspended' since this does not seem as though it is being 
actively worked on. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=13997 
>Unformatted:
