Prevent a race between vm_object_collapse() and vm_object_split() from

causing a crash.

Suppose that we have two objects, obj and backing_obj, where
backing_obj is obj's backing object.  Further, suppose that
backing_obj has a reference count of two.  One being the reference
held by obj and the other by a map entry.  Now, suppose that the map
entry is deallocated and its reference removed by
vm_object_deallocate().  vm_object_deallocate() recognizes that the
only remaining reference is from a shadow object, obj, and calls
vm_object_collapse() on obj.  vm_object_collapse() executes

                if (backing_object->ref_count == 1) {
                        /*
                         * If there is exactly one reference to the backing
                         * object, we can collapse it into the parent.
                         */
                        vm_object_backing_scan(object, OBSC_COLLAPSE_WAIT);

vm_object_backing_scan(OBSC_COLLAPSE_WAIT) executes

        if (op & OBSC_COLLAPSE_WAIT) {
                vm_object_set_flag(backing_object, OBJ_DEAD);
        }

Finally, suppose that either vm_object_backing_scan() or
vm_object_collapse() sleeps releasing its locks.  At this instant,
another thread executes vm_object_split().  It crashes in
vm_object_reference_locked() on the assertion that the object is not
dead.  If, however, assertions are not enabled, it crashes much later,
after the object has been recycled, in vm_object_deallocate() because
the shadow count and shadow list are inconsistent.

Reviewed by: tegge
Reported by: jhb
MFC after: 1 week
This commit is contained in:
Alan Cox 2007-03-27 08:55:17 +00:00
parent 45c4f0cbc3
commit 19c244d064

View File

@ -1317,6 +1317,14 @@ vm_object_split(vm_map_entry_t entry)
source = orig_object->backing_object;
if (source != NULL) {
VM_OBJECT_LOCK(source);
if ((source->flags & OBJ_DEAD) != 0) {
VM_OBJECT_UNLOCK(source);
VM_OBJECT_UNLOCK(orig_object);
VM_OBJECT_UNLOCK(new_object);
vm_object_deallocate(new_object);
VM_OBJECT_LOCK(orig_object);
return;
}
LIST_INSERT_HEAD(&source->shadow_head,
new_object, shadow_list);
source->shadow_count++;