kern/64573: mmap with PROT_NONE, but still could be read

Mark W. Krentel krentel at dreamscape.com
Tue Apr 13 18:40:20 PDT 2004


The following reply was made to PR kern/64573; it has been noted by GNATS.

From: "Mark W. Krentel" <krentel at dreamscape.com>
To: bug-followup at freebsd.org
Cc: alc at freebsd.org, hddai at 163.net
Subject: Re: kern/64573: mmap with PROT_NONE, but still could be read
Date: Tue, 13 Apr 2004 21:35:38 -0400

 First, there is a much simpler program to reproduce the bug.  This is
 from 5.2-current as of April 6, 2004.
 
 /*
  * Test mmap() and PROT_NONE.  The first printf() should fail with seg
  * fault or bus error because the segment has protection PROT_NONE,
  * but actually, it runs ok.
  */
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/mman.h>
 
 int main()
 {
 	int   fd;
 	char *p;
 
 	fd = open("myfile", O_RDWR);
 	if (fd < 0) err(1, "open failed");
 
 	p = mmap(NULL, 4096, PROT_NONE, MAP_SHARED, fd, 0);
 	if (p == MAP_FAILED) err(1, "mmap failed");
 
 	printf("Reading p[4] = %d\n", p[4]);
 	printf("Success\n");
 
 	return 0;
 }
 
 % yes abcdefg | dd of=myfile bs=1024 count=4
 4+0 records in
 4+0 records out
 4096 bytes transferred in 0.000223 secs (18354561 bytes/sec)
 % hd myfile 
 00000000  61 62 63 64 65 66 67 0a  61 62 63 64 65 66 67 0a  |abcdefg.abcdefg.|
 *
 00001000
 % ./a.out 
 Reading p[4] = 101
 Success
 %
 
 The problem is that mmap() calls vm_mmap() and the non-MAP_STACK
 case calls vm_map_find() which calls vm_map_insert() which calls
 vm_map_pmap_enter() which calls pmap_enter_quick() and
 pmap_enter_quick() assumes that the segment always has read access.
 See: pmap_enter_quick() in sys/i386/i386/pmap.c.
 
 A similar bug happens with madvise(..., MADV_WILLNEED).  Change the
 above program to call open() followed by mmap(..., PROT_READ, ...)
 followed by mprotect(..., PROT_NONE).  At this point, reading from the
 segment produces a bus error.  But now add madvise(..., MADV_WILLNEED)
 and you can read the segment.  Again, the problem is that madvise()
 calls vm_map_madvise() and the case for MADV_WILLNEED calls
 vm_map_pmap_enter() as before.
 
 I could only produce the bug with mmap() on a file and with read
 access.  Write access and mmap() with MAP_ANON and MAP_STACK behaved
 as expected.
 
 The patch below adds a parameter "prot" to vm_map_pmap_enter() so that
 vm_map_pmap_enter() will avoid calling pmap_enter_quick() when the
 segment doesn't have read access.  It makes a bit more sense for
 vm_map_pmap_enter() to make this decision rather than the calling
 program, and prot may be useful for other things.
 
 With much guidence from: alc
 
 --Mark
 
 ----------------
 
 --- vm_map.h.orig	Tue Apr  6 16:21:07 2004
 +++ vm_map.h	Tue Apr 13 17:16:14 2004
 @@ -337,7 +337,7 @@
      vm_pindex_t *, vm_prot_t *, boolean_t *);
  void vm_map_lookup_done (vm_map_t, vm_map_entry_t);
  boolean_t vm_map_lookup_entry (vm_map_t, vm_offset_t, vm_map_entry_t *);
 -void vm_map_pmap_enter(vm_map_t map, vm_offset_t addr,
 +void vm_map_pmap_enter(vm_map_t map, vm_offset_t addr, vm_prot_t prot,
      vm_object_t object, vm_pindex_t pindex, vm_size_t size, int flags);
  int vm_map_protect (vm_map_t, vm_offset_t, vm_offset_t, vm_prot_t, boolean_t);
  int vm_map_remove (vm_map_t, vm_offset_t, vm_offset_t);
 
 --- vm_map.c.orig	Tue Apr  6 16:21:07 2004
 +++ vm_map.c	Tue Apr 13 17:53:27 2004
 @@ -881,7 +881,7 @@
  #endif
  
  	if (cow & (MAP_PREFAULT|MAP_PREFAULT_PARTIAL)) {
 -		vm_map_pmap_enter(map, start,
 +		vm_map_pmap_enter(map, start, prot,
  				    object, OFF_TO_IDX(offset), end - start,
  				    cow & MAP_PREFAULT_PARTIAL);
  	}
 @@ -1252,14 +1252,14 @@
   *	immediately after an mmap(2).
   */
  void
 -vm_map_pmap_enter(vm_map_t map, vm_offset_t addr,
 +vm_map_pmap_enter(vm_map_t map, vm_offset_t addr, vm_prot_t prot,
      vm_object_t object, vm_pindex_t pindex, vm_size_t size, int flags)
  {
  	vm_offset_t tmpidx;
  	int psize;
  	vm_page_t p, mpte;
  
 -	if (object == NULL)
 +	if ((object == NULL) || ((prot & VM_PROT_READ) == 0))
  		return;
  	mtx_lock(&Giant);
  	VM_OBJECT_LOCK(object);
 @@ -1551,6 +1551,7 @@
  			if (behav == MADV_WILLNEED) {
  				vm_map_pmap_enter(map,
  				    useStart,
 +				    current->protection,
  				    current->object.vm_object,
  				    pindex,
  				    (count << PAGE_SHIFT),


More information about the freebsd-bugs mailing list