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