mlock & COW

Jonathan Chen jon at freebsd.org
Sun Mar 9 21:57:04 UTC 2008


I've been battling with a bug related to mlock and COW pages.  Since I'm 
basically clueless when it comes to the VM subsystem, I was hoping someone 
with more clue can look at my fix and let me know if I'm doing the right 
thing here.

The problem: User programs will crash (SEGV/BUS) if COW pages become 
writable after the pages are mlocked.  This happens if shared libraries is 
loaded in a program after a call to mlockall(MCL_FUTURE), and can be seen 
with amd and ntpd when nss_ldap is used in the system.  Included at the end 
of this message is a sample program that demonstrates the problem.

The "solution": Forcibly wire the page via vm_fault_wire() when the page 
protection bits are changed.  This seems to make the problem disappear, but 
I'm not sure if causing a fault on vm_map_protect() is a good thing to do.  
Am I correct in thinking vm_fault_wire() will cause the COW page to be 
copied immediately?  I think this is the right thing to do, given the page 
is supposed to be wired, but I'm not sure if somewhere in the vm fault 
routines might be a better place to put the fix.  I'm also not sure what 
the last argument to vm_fault_wire() (fictitious) means, I just copied the 
argument from another invocation.  My patch (against 7-STABLE) is included 
below.

I'll commit this if someone blesses the fix as the right thing.


Index: sys/vm/vm_map.c
===================================================================
RCS file: /home/ncvs/src/sys/vm/vm_map.c,v
retrieving revision 1.388.2.3
diff -u -p -r1.388.2.3 vm_map.c
--- sys/vm/vm_map.c	18 Jan 2008 10:02:53 -0000	1.388.2.3
+++ sys/vm/vm_map.c	9 Mar 2008 20:55:50 -0000
@@ -1621,6 +1621,15 @@ vm_map_protect(vm_map_t map, vm_offset_t
 			    current->end,
 			    current->protection & MASK(current));
 #undef	MASK
+			if ((entry->eflags & 
+			    (MAP_ENTRY_USER_WIRED|MAP_ENTRY_COW)) ==
+			    (MAP_ENTRY_USER_WIRED|MAP_ENTRY_COW)) {
+				vm_fault_wire(map, current->start,
+				    current->end, TRUE,
+				    entry->object.vm_object != NULL &&
+				    entry->object.vm_object->type ==
+				    OBJT_DEVICE);
+			}
 		}
 		vm_map_simplify_entry(map, current);
 		current = current->next;




bug demonstration code
===================================================================

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/mman.h>

char *b;

int main() {
    int fd = open("/usr/lib/libc.a", O_RDONLY);
    if (fd < 0) {
	perror("open");
	exit(1);
    }
    b = mmap(0, 1024, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
    if (b == MAP_FAILED) {
	perror("mmap");
	exit(0);
    }
    if (mlock(b, 1024) != 0) {
	perror("mlock");
    }
    if (mprotect(b, 1024, PROT_READ|PROT_WRITE|PROT_EXEC) != 0) {
	perror("mprotect");
    }
    printf("memset crash now\n");
    memset(b, 1, 1024);
    printf("still alive\n");
}

-Jon


More information about the freebsd-hackers mailing list