Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 17 Aug 2004 21:40:01 +0200
From:      Uwe Doering <gemini@geminix.org>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/70587: NULL pointer dereference in vm_pageout_scan()
Message-ID:  <E1Bx9oP-0006aG-00@geminix.org>
Resent-Message-ID: <200408171940.i7HJeLw0071491@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         70587
>Category:       kern
>Synopsis:       NULL pointer dereference in vm_pageout_scan()
>Confidential:   no
>Severity:       critical
>Priority:       high
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Aug 17 19:40:21 GMT 2004
>Closed-Date:
>Last-Modified:
>Originator:     Uwe Doering
>Release:        FreeBSD 4.5-RELEASE i386
>Organization:
EscapeBox - Managed On-Demand UNIX Servers
http://www.escapebox.net

>Environment:
System: FreeBSD geminix.org 4.5-RELEASE FreeBSD 4.5-RELEASE #1: Sun Aug 15 10:59:08 GMT 2004 root@localhost:/STABLE_Enhanced_Edition i386


>Description:
A couple of days ago one of our normally extremely stable server
machines panicked due to a NULL pointer dereference.  While we
didn't get a kernel dump we at least had the instruction pointer
and the offending data address.

After disassembling the respective part of the kernel it became
clear that the pointer in the 'object' field of the relevant
'vm_page_t' structure was NULL at the time and was beeing used
without checking it for NULL first.  Here's the section of code
where it happened (in vm_pageout.c:vm_pageout_scan()):

        /*
         * If the object is not being used, we ignore previous 
         * references.
         */
        if (m->object->ref_count == 0) {
                vm_page_flag_clear(m, PG_REFERENCED);
                pmap_clear_reference(m);

Now, the original assumption when this code had been written
may well have been that it can never happen that a page on the
inactive queue is _not_ associated with an object.  The crash
we experienced unfortunately proves the opposite.  And we also
found that other parts of the kernel certainly don't trust the
'object' field blindly.

>How-To-Repeat:
I have no idea how to repeat that condition.  We are running
several servers for over two years in production now, and this
was the first time it happend to us.  I speculate that the
'object' field being NULL is just a transitory state that
became apparent due to a race condition.  Otherwise it should
have hit us more frequently in the past.

>Fix:
Please consider adopting the patch below.  We take the pragmatic
approach and skip the page if it isn't associated with an object,
on the assumption that this state will be short-lived, and also
because in this context we wouldn't know what to do with a page
like this, anyway.  The patch deals with the scanning loops for
both the inactive and active queue.

--- vm_pageout.c.diff begins here ---
--- src/sys/vm/vm_pageout.c.orig	Mon Mar 11 16:48:15 2002
+++ src/sys/vm/vm_pageout.c	Mon Aug  2 13:30:57 2004
@@ -704,7 +704,7 @@
 		/*
 		 * A held page may be undergoing I/O, so skip it.
 		 */
-		if (m->hold_count) {
+		if (m->hold_count || m->object == NULL) {
 			s = splvm();
 			TAILQ_REMOVE(&vm_page_queues[PQ_INACTIVE].pl, m, pageq);
 			TAILQ_INSERT_TAIL(&vm_page_queues[PQ_INACTIVE].pl, m, pageq);
@@ -988,7 +988,8 @@
 		 */
 		if ((m->busy != 0) ||
 		    (m->flags & PG_BUSY) ||
-		    (m->hold_count != 0)) {
+		    (m->hold_count != 0) ||
+		    (m->object == NULL)) {
 			s = splvm();
 			TAILQ_REMOVE(&vm_page_queues[PQ_ACTIVE].pl, m, pageq);
 			TAILQ_INSERT_TAIL(&vm_page_queues[PQ_ACTIVE].pl, m, pageq);
--- vm_pageout.c.diff ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E1Bx9oP-0006aG-00>