From owner-freebsd-hackers@FreeBSD.ORG Sun Mar 9 21:57:04 2008 Return-Path: Delivered-To: hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BF3031065673 for ; Sun, 9 Mar 2008 21:57:04 +0000 (UTC) (envelope-from jon@porthos.spock.org) Received: from porthos.spock.org (porthos.spock.org [204.97.176.45]) by mx1.freebsd.org (Postfix) with ESMTP id 4A9908FC14 for ; Sun, 9 Mar 2008 21:57:04 +0000 (UTC) (envelope-from jon@porthos.spock.org) Received: from porthos.spock.org (R00+@localhost [127.0.0.1]) by porthos.spock.org with ESMTP serial EF600Q3T-B7F8823m29LOgCx057329F7T for ; Sun, 9 Mar 2008 17:24:42 -0400 (EDT) (envelope-from jon@porthos.spock.org) Received: (from jon@localhost) by porthos.spock.org (8.14.2/8.14.2/Submit) id m29LOgXV057328 for hackers@freebsd.org; Sun, 9 Mar 2008 17:24:42 -0400 (EDT) (envelope-from jon) Date: Sun, 9 Mar 2008 17:24:42 -0400 From: Jonathan Chen To: hackers@freebsd.org Message-ID: <20080309212441.GA56523@porthos.spock.org> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.17 (2007-11-01) X-Scanned-By: MIMEDefang 2.63 on 204.97.176.45 Cc: Subject: mlock & COW X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 09 Mar 2008 21:57:04 -0000 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 #include #include #include #include #include #include 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