On Thu, Feb 03, 2022 at 04:49:26PM +0000, Klemens Nanni wrote:
> On Mon, Jan 31, 2022 at 11:11:25AM -0300, Martin Pieuchot wrote:
> > On 31/01/22(Mon) 10:24, Klemens Nanni wrote:
> > > Running with my uvm assert diff showed that uvm_fault_unwire_locked()
> > > was called without any locks held.
> > > 
> > > This happened when I rebooted my machine:
> > > 
> > >   uvm_fault_unwire_locked()
> > >   uvm_unmap_kill_entry_withlock()
> > >   uvm_unmap_kill_entry()
> > >   uvm_map_teardown()
> > >   uvmspace_free()
> > > 
> > > This code does not corrupt anything because
> > > uvm_unmap_kill_entry_withlock() is grabbing the kernel lock aorund its
> > > uvm_fault_unwire_locked() call.
> > > 
> > > But regardless of the kernel lock dances in this code path, the uvm map
> > > ought to be protected by its own lock.  uvm_fault_unwire() does that.
> > > 
> > > uvm_fault_unwire_locked()'s comment says the map must at least be read
> > > locked, which is what all other code paths to that function do.
> > > 
> > > This makes my latest assert diff happy in the reboot case (it did not
> > > always hit that assert).
> > 
> > I'm happy your asserts found a first bug.
> > 
> > I"m afraid calling this function below could result in a grabbing the
> > lock twice.  Can we be sure this doesn't happen?
> 
> I see two call paths leading to uvm_unmap_kill_entry():
> 
> 1. uvm_unmap_remove():  map must already be locked by the caller
> 2. uvmspace_free() -> uvm_map_teardown():  the vmspace is refcounted and
>    the kernel lock must be held.
> 
> I don't see how we can reach uvm_unmap_kill_entry() with another thread
> holding a lock to the vm_map backing the address space, for example.

> > uvm_unmap_kill_entry() is called in many different contexts and this is
> > currently a mess.  I don't know what NetBSD did in this area but it is
> > worth looking at and see if there isn't a good idea to untangle this.
> 
> Yes, reasoning about this with confidence is hard and I'm still
> hestitant to do so, but all my use/test cases so far failed to hit
> further lock assertions with uvm_unmap_kill_entry_withlock() locking the
> map, while running it unlocked would reliably panic.

... with the lock grabbed in uvm_map_teardown() that is, otherwise
the first call path can lock against itself (regress/misc/posixtestsuite
is a reproduce for this):

        vm_map_lock_read_ln+0x38
        uvm_fault_unwire+0x58
        uvm_unmap_kill_entry_withlock+0x68
        uvm_unmap_remove+0x2d4
        sys_munmap+0x11c

which is obvious in hindsight.

So grabbing the lock in uvm_map_teardown() instead avoids that while
still ensuring a locked map in the path missing a lock.

> I'm still going through NetBSD's changes in this regard...

Index: uvm_map.c
===================================================================
RCS file: /cvs/src/sys/uvm/uvm_map.c,v
retrieving revision 1.282
diff -u -p -r1.282 uvm_map.c
--- uvm_map.c   21 Dec 2021 22:21:32 -0000      1.282
+++ uvm_map.c   4 Feb 2022 02:51:00 -0000
@@ -2734,6 +2751,7 @@ uvm_map_teardown(struct vm_map *map)
        KERNEL_ASSERT_UNLOCKED();
 
        KASSERT((map->flags & VM_MAP_INTRSAFE) == 0);
+       vm_map_lock(map);
 
        /* Remove address selectors. */
        uvm_addr_destroy(map->uaddr_exe);

Reply via email to