kern/68017: fork with INHERIT_NONE miscounts VM map sizes
Mark W. Krentel
krentel at dreamscape.com
Wed Jun 16 18:00:42 GMT 2004
>Number: 68017
>Category: kern
>Synopsis: fork with INHERIT_NONE miscounts VM map sizes
>Confidential: no
>Severity: serious
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: sw-bug
>Submitter-Id: current-users
>Arrival-Date: Wed Jun 16 18:00:40 GMT 2004
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:
More information about the freebsd-bugs
mailing list