Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 27 Sep 1999 20:12:19 GMT
From:      abb@zenon.net
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/13997: RLIMIT_NPROC works unadequately for jails (patch included)
Message-ID:  <199909272012.UAA13312@abb.zenon.net>

next in thread | raw e-mail | index | archive | help

>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 <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:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199909272012.UAA13312>