From nobody@FreeBSD.org  Wed Jun 16 17:53:55 2004
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 5A1E816A4FC
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 16 Jun 2004 17:53:55 +0000 (GMT)
Received: from www.freebsd.org (www.freebsd.org [216.136.204.117])
	by mx1.FreeBSD.org (Postfix) with ESMTP id 3DB1B43D1F
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 16 Jun 2004 17:53:55 +0000 (GMT)
	(envelope-from nobody@FreeBSD.org)
Received: from www.freebsd.org (localhost [127.0.0.1])
	by www.freebsd.org (8.12.11/8.12.11) with ESMTP id i5GHrivJ097900
	for <freebsd-gnats-submit@FreeBSD.org>; Wed, 16 Jun 2004 17:53:44 GMT
	(envelope-from nobody@www.freebsd.org)
Received: (from nobody@localhost)
	by www.freebsd.org (8.12.11/8.12.11/Submit) id i5GHrh5D097899;
	Wed, 16 Jun 2004 17:53:43 GMT
	(envelope-from nobody)
Message-Id: <200406161753.i5GHrh5D097899@www.freebsd.org>
Date: Wed, 16 Jun 2004 17:53:43 GMT
From: "Mark W. Krentel" <krentel@dreamscape.com>
To: freebsd-gnats-submit@FreeBSD.org
Subject: fork with INHERIT_NONE miscounts VM map sizes
X-Send-Pr-Version: www-2.3

>Number:         68017
>Category:       kern
>Synopsis:       fork with INHERIT_NONE miscounts VM map sizes
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    green
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jun 16 18:00:40 GMT 2004
>Closed-Date:    Mon Aug 16 11:14:04 GMT 2004
>Last-Modified:  Mon Aug 16 11:14:04 GMT 2004
>Originator:     Mark W. Krentel
>Release:        5.2-Current as of May 26, 2004
>Organization:
none
>Environment:
5.2-Current as of May 26, 2004
>Description:
During fork(), vmspace_fork() in sys/vm/vm_map.c constructs the new
vmspace and vm_map by copying map entries according to their
inheritance.  Map entries with VM_INHERIT_NONE are correctly skipped,
but the size of the new map is incorrectly and always set to the size
of the old map, thus ingoring the INHERIT_NONE regions.

    new_map->size = old_map->size;

This results in a new size that is too large whenever the original
process has any region with inheritance INHERIT_NONE.

Furthermore, the following fields in struct vmspace are also bcopy()'d
directly from the old vmspace without regard to the inheritance of the
segments.

    #define vm_startcopy vm_rssize
            segsz_t vm_rssize;      /* current resident set size in pages */
            segsz_t vm_swrss;       /* resident set size before last swap */
            segsz_t vm_tsize;       /* text size (pages) XXX */
            segsz_t vm_dsize;       /* data size (pages) XXX */
            segsz_t vm_ssize;       /* stack size (pages) */
            caddr_t vm_taddr;       /* (c) user virtual address of text */
            caddr_t vm_daddr;       /* (c) user virtual address of data */
            caddr_t vm_maxsaddr;    /* user VA at max stack growth */
    #define vm_endcopy vm_exitingcnt

vm_taddr and vm_daddr are probably ok, but some of the others may also
be incorrect if the original process has regions with INHERIT_NONE.

Btw, nentries in struct vm_map is computed correctly because it is
initialized to 0 in vm_map_zinit() and then incremented in
vm_map_entry_link().

I guess no one ever uses INHERIT_NONE.  In 5.2 with INVARIANTS turned
on, vm_map_zdtor() notices the discrepancy in vm_map.size and panics,
which is pretty noticeable.

>How-To-Repeat:
In 5.2 with INVARIANTS turned on, the following program produces a
panic in vm_map_zdtor() because map->size > 0 when the child exits.

#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
        void *p;

        p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0);
        minherit(p, 4096, INHERIT_NONE);
        fork();

        return (0);
}

>Fix:
This patch fixes the vm_map.size problem.  Note that size (along with
nentries) is initialized to 0 in vm_map_zinit().

I don't really know enough about the other vmspace entries (vm_tsize,
vm_dsize, etc.) to offer an intelligent patch for them.

Index: sys/vm/vm_map.c
===================================================================
RCS file: /data/ncvs/src/sys/vm/vm_map.c,v
retrieving revision 1.338
diff -u -r1.338 vm_map.c
--- sys/vm/vm_map.c     25 May 2004 18:28:52 -0000      1.338
+++ sys/vm/vm_map.c     16 Jun 2004 17:22:49 -0000
@@ -2431,6 +2431,7 @@
                         */
                        vm_map_entry_link(new_map, new_map->header.prev,
                            new_entry);
+                       new_map->size += new_entry->end - new_entry->start;
 
                        /*
                         * Update the physical map
@@ -2452,6 +2453,7 @@
                        new_entry->object.vm_object = NULL;
                        vm_map_entry_link(new_map, new_map->header.prev,
                            new_entry);
+                       new_map->size += new_entry->end - new_entry->start;
                        vm_map_copy_entry(old_map, new_map, old_entry,
                            new_entry);
                        break;
@@ -2459,7 +2461,6 @@
                old_entry = old_entry->next;
        }
 
-       new_map->size = old_map->size;
        old_map->infork = 0;
        vm_map_unlock(old_map);

>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->green 
Responsible-Changed-By: bms 
Responsible-Changed-When: Wed Jun 23 06:57:42 GMT 2004 
Responsible-Changed-Why:  
green@ was here very recently 

http://www.freebsd.org/cgi/query-pr.cgi?pr=68017 

From: Brian Fundakowski Feldman <green@FreeBSD.org>
To: Bruce M Simpson <bms@FreeBSD.org>
Cc: alc@FreeBSD.org, freebsd-bugs@FreeBSD.org
Subject: Re: kern/68017: fork with INHERIT_NONE miscounts VM map sizes
Date: Wed, 23 Jun 2004 12:32:30 -0400

 Please try this (admittedly untested) patch and see if it fixes the
 problems you're having.  It should help clean up the rest of the
 vmspace properties when forking as well, so if you were to uninherit
 stack or data or text pages, they should no longer count against the
 process's VM limits.
 
 Index: vm_map.c
 ===================================================================
 RCS file: /usr/ncvs/src/sys/vm/vm_map.c,v
 retrieving revision 1.338
 diff -u -r1.338 vm_map.c
 --- vm_map.c	25 May 2004 18:28:52 -0000	1.338
 +++ vm_map.c	23 Jun 2004 16:29:37 -0000
 @@ -2342,12 +2342,44 @@
  }
  
  /*
 + * vmspace_map_entry_forked:
 + * Update the newly-forked vmspace each time a map entry is inherited
 + * or copied.  The values for vm_dsize and vm_tsize are approximate
 + * (and mostly-obsolete ideas in the face of mmap(2) et al.)
 + */
 +static void
 +vmspace_map_entry_forked(const struct vmspace *vm1, struct vmspace *vm2,
 +    vm_map_entry_t entry)
 +{
 +	vm_size_t entrysize;
 +	vm_offset_t newend;
 +
 +	entrysize = entry->end - entry->start;
 +	vm2->vm_map.size += entrysize;
 +	if (entry->eflags & (MAP_ENTRY_GROWS_DOWN | MAP_ENTRY_GROWS_UP)) {
 +		vm2->vm_ssize += btoc(entrysize);
 +	} else if (entry->start >= (vm_offset_t)vm1->vm_daddr &&
 +	    entry->start < (vm_offset_t)vm1->vm_daddr + ctob(vm1->vm_dsize)) {
 +		newend = min(entry->end,
 +		    (vm_offset_t)vm1->vm_daddr + ctob(vm1->vm_dsize));
 +		vm2->vm_dsize += btoc(newend - entry->start);
 +	} else if (entry->start >= (vm_offset_t)vm1->vm_taddr &&
 +	    entry->start < (vm_offset_t)vm1->vm_taddr + ctob(vm1->vm_tsize)) {
 +		newend = min(entry->end,
 +		    (vm_offset_t)vm1->vm_taddr + ctob(vm1->vm_tsize));
 +		vm2->vm_tsize += btoc(newend - entry->start);
 +	}
 +}
 +
 +/*
   * vmspace_fork:
   * Create a new process vmspace structure and vm_map
   * based on those of an existing process.  The new map
   * is based on the old map, according to the inheritance
   * values on the regions in that map.
   *
 + * XXX It might be worth coalescing the entries added to the new vmspace.
 + *
   * The source map must not be locked.
   */
  struct vmspace *
 @@ -2366,8 +2398,16 @@
  	old_map->infork = 1;
  
  	vm2 = vmspace_alloc(old_map->min_offset, old_map->max_offset);
 -	bcopy(&vm1->vm_startcopy, &vm2->vm_startcopy,
 -	    (caddr_t) &vm1->vm_endcopy - (caddr_t) &vm1->vm_startcopy);
 +	/*
 +	 * Using vm_{start,end}copy is invalid for more fields than
 +	 * it is valid here.  For the most part, initialize size
 +	 * fields to zero and update them as map entries are copied
 +	 */
 +	bzero(&vm2->vm_startcopy,
 +	    (caddr_t)&vm2->vm_endcopy - (caddr_t)&vm2->vm_startcopy);
 +	vm2->vm_taddr = vm1->vm_taddr;
 +	vm2->vm_daddr = vm1->vm_daddr;
 +	vm2->vm_maxsaddr = vm1->vm_maxsaddr;
  	new_map = &vm2->vm_map;	/* XXX */
  	new_map->timestamp = 1;
  
 @@ -2431,6 +2471,7 @@
  			 */
  			vm_map_entry_link(new_map, new_map->header.prev,
  			    new_entry);
 +			vmspace_map_entry_forked(vm1, vm2, new_entry);
  
  			/*
  			 * Update the physical map
 @@ -2452,6 +2493,7 @@
  			new_entry->object.vm_object = NULL;
  			vm_map_entry_link(new_map, new_map->header.prev,
  			    new_entry);
 +			vmspace_map_entry_forked(vm1, vm2, new_entry);
  			vm_map_copy_entry(old_map, new_map, old_entry,
  			    new_entry);
  			break;
 @@ -2459,7 +2501,6 @@
  		old_entry = old_entry->next;
  	}
  
 -	new_map->size = old_map->size;
  	old_map->infork = 0;
  	vm_map_unlock(old_map);
  
 
 -- 
 Brian Fundakowski Feldman                           \'[ FreeBSD ]''''''''''\
   <> green@FreeBSD.org                               \  The Power to Serve! \
  Opinions expressed are my own.                       \,,,,,,,,,,,,,,,,,,,,,,\

From: "Mark W. Krentel" <krentel@dreamscape.com>
To: bug-followup@freebsd.org
Cc: alc@freebsd.org, green@freebsd.org
Subject: Re: kern/68017: fork with INHERIT_NONE miscounts VM map sizes 
Date: Fri, 25 Jun 2004 01:09:27 -0400

 Ok, I've tested Brian's patch and it compiles and avoids the panic I
 described above.
 
 Actually, I don't have a specific application that uses INHERIT_NONE
 or even a test program for the other vmspace entries.  But the code
 looks ok and I trust Alan and Brian's judgement, so I'm satisfied.
 
 --Mark
State-Changed-From-To: open->closed 
State-Changed-By: green 
State-Changed-When: Mon Aug 16 11:13:35 GMT 2004 
State-Changed-Why:  
This should now be fixed in -CURRENT.  Thank you very much for your PR. 

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