Author: kib
Date: Thu Oct  8 11:07:09 2015
New Revision: 289026
URL: https://svnweb.freebsd.org/changeset/base/289026

Log:
  Enforce the maxproc limitation before allocating struct proc, initial
  struct thread and kernel stack for the thread.  Otherwise, a load
  similar to a fork bomb would exhaust KVA and possibly kmem, mostly due
  to the struct proc being type-stable.
  
  The nprocs counter is changed from being protected by allproc_lock sx
  to be an atomic variable.  Note that ddb/db_ps.c:db_ps() use of nprocs
  was unsafe before, and is still unsafe, but it seems that the only
  possible undesired consequence is the harmless warning printed when
  allproc linked list length does not match nprocs.
  
  Diagnosed by: Svatopluk Kraus <onw...@gmail.com>
  Tested by:    pho
  Sponsored by: The FreeBSD Foundation
  MFC after:    1 week

Modified:
  head/sys/kern/kern_exit.c
  head/sys/kern/kern_fork.c

Modified: head/sys/kern/kern_exit.c
==============================================================================
--- head/sys/kern/kern_exit.c   Thu Oct  8 10:00:41 2015        (r289025)
+++ head/sys/kern/kern_exit.c   Thu Oct  8 11:07:09 2015        (r289026)
@@ -963,9 +963,7 @@ proc_reap(struct thread *td, struct proc
        KASSERT(FIRST_THREAD_IN_PROC(p),
            ("proc_reap: no residual thread!"));
        uma_zfree(proc_zone, p);
-       sx_xlock(&allproc_lock);
-       nprocs--;
-       sx_xunlock(&allproc_lock);
+       atomic_add_int(&nprocs, -1);
 }
 
 static int

Modified: head/sys/kern/kern_fork.c
==============================================================================
--- head/sys/kern/kern_fork.c   Thu Oct  8 10:00:41 2015        (r289025)
+++ head/sys/kern/kern_fork.c   Thu Oct  8 11:07:09 2015        (r289026)
@@ -382,12 +382,6 @@ do_fork(struct thread *td, int flags, st
        p2_held = 0;
        p1 = td->td_proc;
 
-       /*
-        * Increment the nprocs resource before blocking can occur.  There
-        * are hard-limits as to the number of processes that can run.
-        */
-       nprocs++;
-
        trypid = fork_findpid(flags);
 
        sx_sunlock(&proctree_lock);
@@ -771,16 +765,14 @@ int
 fork1(struct thread *td, int flags, int pages, struct proc **procp,
     int *procdescp, int pdflags, struct filecaps *fcaps)
 {
-       struct proc *p1;
-       struct proc *newproc;
-       int ok;
+       struct proc *p1, *newproc;
        struct thread *td2;
        struct vmspace *vm2;
+       struct file *fp_procdesc;
        vm_ooffset_t mem_charged;
-       int error;
+       int error, nprocs_new, ok;
        static int curfail;
        static struct timeval lastfail;
-       struct file *fp_procdesc = NULL;
 
        /* Check for the undefined or unimplemented flags. */
        if ((flags & ~(RFFLAGS | RFTSIGFLAGS(RFTSIGMASK))) != 0)
@@ -819,6 +811,35 @@ fork1(struct thread *td, int flags, int 
                return (fork_norfproc(td, flags));
        }
 
+       fp_procdesc = NULL;
+       newproc = NULL;
+       vm2 = NULL;
+
+       /*
+        * Increment the nprocs resource before allocations occur.
+        * Although process entries are dynamically created, we still
+        * keep a global limit on the maximum number we will
+        * create. There are hard-limits as to the number of processes
+        * that can run, established by the KVA and memory usage for
+        * the process data.
+        *
+        * Don't allow a nonprivileged user to use the last ten
+        * processes; don't let root exceed the limit.
+        */
+       nprocs_new = atomic_fetchadd_int(&nprocs, 1) + 1;
+       if ((nprocs_new >= maxproc - 10 && priv_check_cred(td->td_ucred,
+           PRIV_MAXPROC, 0) != 0) || nprocs_new >= maxproc) {
+               error = EAGAIN;
+               sx_xlock(&allproc_lock);
+               if (ppsratecheck(&lastfail, &curfail, 1)) {
+                       printf("maxproc limit exceeded by uid %u (pid %d); "
+                           "see tuning(7) and login.conf(5)\n",
+                           td->td_ucred->cr_ruid, p1->p_pid);
+               }
+               sx_xunlock(&allproc_lock);
+               goto fail2;
+       }
+
        /*
         * If required, create a process descriptor in the parent first; we
         * will abandon it if something goes wrong. We don't finit() until
@@ -831,7 +852,6 @@ fork1(struct thread *td, int flags, int 
        }
 
        mem_charged = 0;
-       vm2 = NULL;
        if (pages == 0)
                pages = kstack_pages;
        /* Allocate new proc. */
@@ -898,20 +918,7 @@ fork1(struct thread *td, int flags, int 
 
        /* We have to lock the process tree while we look for a pid. */
        sx_slock(&proctree_lock);
-
-       /*
-        * Although process entries are dynamically created, we still keep
-        * a global limit on the maximum number we will create.  Don't allow
-        * a nonprivileged user to use the last ten processes; don't let root
-        * exceed the limit. The variable nprocs is the current number of
-        * processes, maxproc is the limit.
-        */
        sx_xlock(&allproc_lock);
-       if ((nprocs >= maxproc - 10 && priv_check_cred(td->td_ucred,
-           PRIV_MAXPROC, 0) != 0) || nprocs >= maxproc) {
-               error = EAGAIN;
-               goto fail;
-       }
 
        /*
         * Increment the count of procs running with this uid. Don't allow
@@ -942,11 +949,7 @@ fork1(struct thread *td, int flags, int 
        }
 
        error = EAGAIN;
-fail:
        sx_sunlock(&proctree_lock);
-       if (ppsratecheck(&lastfail, &curfail, 1))
-               printf("maxproc limit exceeded by uid %u (pid %d); see 
tuning(7) and login.conf(5)\n",
-                   td->td_ucred->cr_ruid, p1->p_pid);
        sx_xunlock(&allproc_lock);
 #ifdef MAC
        mac_proc_destroy(newproc);
@@ -963,6 +966,7 @@ fail2:
                fdclose(td, fp_procdesc, *procdescp);
                fdrop(fp_procdesc, td);
        }
+       atomic_add_int(&nprocs, -1);
        pause("fork", hz / 2);
        return (error);
 }
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to