Patch from: Eric Lammerts On Tue, 28 Jan 2003, Brian Sullivan wrote: > The problem I am experiencing is that after a certain number of mounts I > get the error message "fork: Resource temporarily unavailable" on the > command line. > After much trouble shooting I realized that number of mounts/umount > sequences I am limited to is the max number of processes for my user id. I > confirmed this by using the "ulimit -u" command to lower my process limit. > It appears the mount command is leaving someting in some sort of process > table in kernel memory (nothing shows in ps or top or in /proc/#### as > being left behind). Has anybody any sort of experience with this at all? > Any suggestions? It's a kernel bug (and easy to reproduce). Every time you do a loop mount, a kernel thread is started (those processes are called "loop0", "loop1", etc.). The problem is that when it starts, it's counted as one of your processes *). Then, it's changed to be a root-owned process without correcting that count **). Patch below fixes the problem. It moves the bookkeeping of changing current->user to a new function switch_uid() (which is now also used by exec_usermodehelper() in kmod.c). The patch is tested. Eric *) "atomic_inc(&p->user->processes);" in do_fork(). **) "this_task->user = INIT_USER;" in reparent_to_init(). linux/sched.h | 1 + exit.c | 2 +- kmod.c | 10 +--------- sched.c | 0 sys.c | 13 ++----------- user.c | 17 +++++++++++++++++ 6 files changed, 22 insertions(+), 21 deletions(-) diff -puN include/linux/sched.h~user-process-count-leak include/linux/sched.h --- 25/include/linux/sched.h~user-process-count-leak 2003-02-09 18:25:42.000000000 -0800 +++ 25-akpm/include/linux/sched.h 2003-02-09 18:25:42.000000000 -0800 @@ -511,6 +511,7 @@ extern void __set_special_pids(pid_t ses /* per-UID process charging. */ extern struct user_struct * alloc_uid(uid_t); extern void free_uid(struct user_struct *); +extern void switch_uid(struct user_struct *); #include diff -puN kernel/kmod.c~user-process-count-leak kernel/kmod.c --- 25/kernel/kmod.c~user-process-count-leak 2003-02-09 18:25:42.000000000 -0800 +++ 25-akpm/kernel/kmod.c 2003-02-09 18:25:42.000000000 -0800 @@ -121,15 +121,7 @@ int exec_usermodehelper(char *program_pa if (curtask->files->fd[i]) close(i); } - /* Drop the "current user" thing */ - { - struct user_struct *user = curtask->user; - curtask->user = INIT_USER; - atomic_inc(&INIT_USER->__count); - atomic_inc(&INIT_USER->processes); - atomic_dec(&user->processes); - free_uid(user); - } + switch_uid(INIT_USER); /* Give kmod all effective privileges.. */ curtask->euid = curtask->fsuid = 0; diff -puN kernel/sched.c~user-process-count-leak kernel/sched.c diff -puN kernel/sys.c~user-process-count-leak kernel/sys.c --- 25/kernel/sys.c~user-process-count-leak 2003-02-09 18:25:42.000000000 -0800 +++ 25-akpm/kernel/sys.c 2003-02-09 18:25:42.000000000 -0800 @@ -561,19 +561,12 @@ asmlinkage long sys_setgid(gid_t gid) static int set_user(uid_t new_ruid, int dumpclear) { - struct user_struct *new_user, *old_user; + struct user_struct *new_user; - /* What if a process setreuid()'s and this brings the - * new uid over his NPROC rlimit? We can check this now - * cheaply with the new uid cache, so if it matters - * we should be checking for it. -DaveM - */ new_user = alloc_uid(new_ruid); if (!new_user) return -EAGAIN; - old_user = current->user; - atomic_dec(&old_user->processes); - atomic_inc(&new_user->processes); + switch_uid(new_user); if(dumpclear) { @@ -581,8 +574,6 @@ static int set_user(uid_t new_ruid, int wmb(); } current->uid = new_ruid; - current->user = new_user; - free_uid(old_user); return 0; } diff -puN kernel/user.c~user-process-count-leak kernel/user.c --- 25/kernel/user.c~user-process-count-leak 2003-02-09 18:25:42.000000000 -0800 +++ 25-akpm/kernel/user.c 2003-02-09 18:25:42.000000000 -0800 @@ -116,6 +116,23 @@ struct user_struct * alloc_uid(uid_t uid return up; } +void switch_uid(struct user_struct *new_user) +{ + struct user_struct *old_user; + + /* What if a process setreuid()'s and this brings the + * new uid over his NPROC rlimit? We can check this now + * cheaply with the new uid cache, so if it matters + * we should be checking for it. -DaveM + */ + old_user = current->user; + atomic_inc(&new_user->__count); + atomic_inc(&new_user->processes); + atomic_dec(&old_user->processes); + current->user = new_user; + free_uid(old_user); +} + static int __init uid_cache_init(void) { diff -puN kernel/exit.c~user-process-count-leak kernel/exit.c --- 25/kernel/exit.c~user-process-count-leak 2003-02-09 18:25:42.000000000 -0800 +++ 25-akpm/kernel/exit.c 2003-02-09 18:25:42.000000000 -0800 @@ -249,7 +249,7 @@ void reparent_to_init(void) /* signals? */ security_task_reparent_to_init(current); memcpy(current->rlim, init_task.rlim, sizeof(*(current->rlim))); - current->user = INIT_USER; + switch_uid(INIT_USER); write_unlock_irq(&tasklist_lock); } _